/*
 * Ingenic IMP AI implement.

 * Copyright (C) 2017 Ingenic Semiconductor Co.,Ltd

 */

#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#define __USE_GNU
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/soundcard.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>
#include <imp/imp_system.h>
#include <system/system.h>
#include <dsystem/funcs.h>
#include "audio_common.h"
#include "abuf.h"
//#include "dmic.h"
#include "audio_process.h"
#include "audioProcess/aec/profiles/profiles_management.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define  TAG "ai"


static char profile_buff[101] = "";

#define AI_SOURCE_RECORD_FILE "ai_source_record.pcm"
#define AI_TARGET_RECORD_FILE "ai_target_record.pcm"
FILE *ai_source_file = NULL;
FILE *ai_target_file = NULL;
volatile int ai_frm_num = 0;

AudioInDev aiDev[MAX_AI_DEV+1];

//extern DmicDev dmicDev[MAX_DMIC_DEV];
#define MIC_NAME	"/dev/dsp"
extern void *g_lib_audioProcess_handle;
extern audio_dev_info_t g_audio_dev_info;

char pathbuff[128]="/etc/webrtc_profile.ini";

/* protect apm destroy */
//static pthread_mutex_t aec_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t ns_mutex = PTHREAD_MUTEX_INITIALIZER;

/*agc function pointer*/
static pFun_agc_create fun_ai_agc_create = NULL;
static pFun_agc_set_config fun_ai_agc_set_config = NULL;
static pFun_agc_get_config fun_ai_agc_get_config = NULL;
static pFun_agc_process fun_ai_agc_process = NULL;
static pFun_agc_free fun_ai_agc_free = NULL;

/*ns function pointer*/
static pFun_ns_create fun_ai_ns_create = NULL;
static pFun_ns_set_config fun_ai_ns_set_config = NULL;
static pFun_ns_get_config fun_ai_ns_get_config = NULL;
static pFun_ns_process fun_ai_ns_process = NULL;
static pFun_ns_free fun_ai_ns_free = NULL;

/*aec function pointer*/
static pFun_aec_create fun_ai_aec_create = NULL;
static pFun_aec_process fun_ai_aec_process = NULL;
static pFun_aec_free fun_ai_aec_free = NULL;

/*hpf function pointer*/
static pFun_hpf_create fun_ai_hpf_create = NULL;
static pFun_hpf_process fun_ai_hpf_process = NULL;
static pFun_hpf_free fun_ai_hpf_free = NULL;

/*hs function pointer*/
static pFun_hs_create fun_ai_hs_create = NULL;
static pFun_hs_process fun_ai_hs_process = NULL;
static pFun_hs_free fun_ai_hs_free = NULL;

NsHandle_t handle_ns;
extern const int16_t kFilterCoefficients8kHz[5];
extern const int16_t kFilterCoefficients[5];
int16_t AiCoefficients[5] = {0};
FilterState handle_ai_hpf;

/* hs */
typedef void Handle_hs;
Handle_hs *handle_ai_hs;

/* agc */
//static int enable_ai_agc_suppression = 0;
typedef void Handle_agc;
Handle_agc *handle_ai_agc;
uint32_t agc_sample_i = 0;

int agc_mode = 0;
extern int64_t timestamp_base;

/*drc function pointer*/
DrcHandle_t handle_ai_drc = {NULL, 0};
static pFun_drc_create fun_ai_drc_create = NULL;
static pFun_drc_set_config fun_ai_drc_set_config = NULL;
static pFun_drc_process fun_ai_drc_process = NULL;
static pFun_drc_free fun_ai_drc_free = NULL;

struct ai_data {
	void *node;
	int64_t timestamp;
	int size;
    void* raw_data;
	int data[0];
};

#define CHECK_RET(r) {if (0 != (ret = r)) {printf("err: %s,%d ret= %d\n", __func__, __LINE__, r); return ret;}}

int impdbg_ai_dev_info(void *buf, int msize, void **arg)
{
	AudioInDev *pdev = NULL;
	dformat *df = NULL;
	char *dbuf  = NULL;
    int len     = 0;

	if(buf == NULL)
		return -1;

	df = buf;
	pdev = &aiDev[1];
	dbuf = (char *)df->out.udf;

	len  =  sprintf(dbuf,"\tDEV(ID:%d)\nAudioIoAttr:\n\tSamplerate:%5d\n\tbitwidth:%5d\n\tsoundmode:%5d\n\tfrmNum:%5d\n\tnumPerFrm:%5d\n\tchnCnt:%5d\n",
					1, pdev->audioIAttr.samplerate, pdev->audioIAttr.bitwidth, pdev->audioIAttr.soundmode, pdev->audioIAttr.frmNum, pdev->audioIAttr.numPerFrm, pdev->audioIAttr.chnCnt);

	len += sprintf(dbuf+len, "ChannalAttr:\n\tminVol:%5d\n\tmaxVol:%5d\n\tsetVol:%5d\n\tGain  :%5d\n",
					pdev->audioInChn[0].vol.minVol, pdev->audioInChn[0].vol.maxVol, pdev->audioInChn[0].ai_vol, pdev->audioInChn[0].ai_ch_gain);

	df->flg |= (1<<8);
	df->out.ret = 0;
	df->out.type = 0;
	df->out.size = len;

	return 0;
}

int impdbg_ai_get_frm(void *buf, int msize, void **arg)
{
	dformat *df = NULL;

	if(buf == NULL)
		return -1;

	df = buf;
	remove(AI_SOURCE_RECORD_FILE);
	remove(AI_TARGET_RECORD_FILE);
	ai_source_file = fopen( AI_SOURCE_RECORD_FILE, "wb");
	if(ai_source_file == NULL) {
		printf("open source file fail\n");
		return -1;
	}
	ai_target_file = fopen( AI_TARGET_RECORD_FILE, "wb");
	if(ai_target_file == NULL) {
		printf("open target file fail\n");
		return -1;
	}
	ai_frm_num = *((int *)(df->in.udf));
	if(ai_frm_num <= 0){
		printf("input frm(%d) err,clear file!!!\n",ai_frm_num);
		fclose(ai_source_file);
		fclose(ai_target_file);
		ai_source_file = NULL;
		ai_target_file = NULL;
		ai_frm_num = 0;
		remove(AI_SOURCE_RECORD_FILE);
		remove(AI_TARGET_RECORD_FILE);
		return -1;
	}else{
		while(ai_frm_num);
		fclose(ai_source_file);
		fclose(ai_target_file);
		ai_source_file = NULL;
		ai_target_file = NULL;
	}
        return 0;
}

void dbg_ai_get_frm(uint32_t* date_ptr,int date_size,file_addr addr)
{
	if(ai_source_file == NULL || ai_target_file == NULL){
		printf("collect file no exit\n");
		return ;
	}else{
		if(date_size == 0){
			 printf("cannot write 0 byte to fail\n");
			  return ;
		}

	}
	if(addr == WRITE_SOURCE){
		fwrite(date_ptr, 1,date_size,ai_source_file);
	}
	else{
		fwrite(date_ptr, 1,date_size,ai_target_file);
	}
}

int __ai_dev_init(AudioInDev *pdev)
{
	int ret = -1;

	struct audio_parameter AiPara;
	pdev->aifd = open(MIC_NAME, O_RDONLY|O_CLOEXEC);
	if (pdev->aifd < 0) {
		IMP_LOG_ERR(TAG, "Open ai dev %s error %s\n", MIC_NAME, strerror(errno));
		return -1;
	}
	AiPara.rate = pdev->audioIAttr.samplerate;
	AiPara.channel = pdev->audioIAttr.chnCnt;
	AiPara.format = 16;
	ret = ioctl(pdev->aifd, AMIC_AI_SET_PARAM, &AiPara);
	if (ret) {
		IMP_LOG_ERR(TAG, "ai Device AMIC_AI_SET_PARAM error.\n");
		return -1;
	}
	ret = ioctl(pdev->aifd, AMIC_AI_ENABLE_STREAM, 1);
	if (ret) {
		IMP_LOG_ERR(TAG, "ai dev AMIC_AI_ENABLE_STREAM error %s\n", strerror(errno));
		return -1;
	}
	return 0;
}

int __ai_dev_deinit(AudioInDev *pdev)
{
	int ret = -1;
	if (pdev->aifd > 0) {
		ret = ioctl(pdev->aifd, AMIC_AI_DISABLE_STREAM, 1);
		if (ret) {
			IMP_LOG_ERR(TAG, "ai dev AMIC_AI_DISABLE_STREAM error %s\n", strerror(errno));
			return -1;
		}
		close(pdev->aifd);
		pdev->aifd = -1;
	}
	return 0;
}

int __ai_dev_set_gain(AudioInDev *pdev, int aigain)
{
	int ret = -1;
	struct volume gainctl;
	gainctl.channel = pdev->audioIAttr.chnCnt;
	gainctl.gain = aigain;
	//gainctl.gain[1] = aigain;
	if ((ret = ioctl(pdev->aifd, AMIC_AI_SET_GAIN, &gainctl)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: set ai front gain %s\n", strerror(errno));
		return -1;
	}
	return ret;
}

int __ai_dev_set_dgain(AudioInDev *pdev, int aidgain)
{
	int ret = -1;
	struct volume gainctl;
	gainctl.channel = pdev->audioIAttr.chnCnt;
	gainctl.gain = aidgain;
	//gainctl.gain = aidgain;
	if ((ret = ioctl(pdev->aifd, AMIC_AI_SET_VOLUME, &gainctl)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: set ai front gain %s\n", strerror(errno));
		return -1;
	}
	return ret;
}

int __ai_dev_set_alcgain(AudioInDev *pdev, int maxgain, int mingain)
{
	int ret = -1;
	struct alc_gain alcgainctl;
	alcgainctl.channel = pdev->audioIAttr.chnCnt;
	alcgainctl.maxgain = maxgain;
	alcgainctl.mingain = mingain;
	//alcgainctl.maxgain[1] = maxgain;
	//alcgainctl.mingain[1] = mingain;
	if ((ret = ioctl(pdev->aifd, AMIC_AI_SET_ALC_GAIN, &alcgainctl)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: set ai agc gain %s\n", strerror(errno));
		return -1;
	}
	return ret;
}

int __ai_dev_enable_aec(AudioInDev *pdev, int *poffset)
{
	int ret = -1;
	int offset = 0;
	if ((ret = ioctl(pdev->aifd, AMIC_ENABLE_AEC, &offset)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: AMIC_AI_ENABLE_AEC %s\n", strerror(errno));
	}
	*poffset = offset;
	return ret;
}

int __ai_dev_disable_aec(AudioInDev *pdev)
{
	int ret = -1;
	int en = 0;
	if ((ret = ioctl(pdev->aifd, AMIC_DISABLE_AEC, &en)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: AMIC_AI_DISABLE_AI %s\n", strerror(errno));
	}
	return ret;
}

int __ai_dev_read(AudioInDev *pdev, AudioInChn *pch, char *buf, char *ref, int aisize, struct timeval *tv)
{
	int retsize;
	int ret = -1;
	struct audio_input_stream ai_stream;
	ai_stream.data = buf;
#if 0
	ret = pthread_mutex_lock(&pdev->mutex_dev);
	if (ret != 0) {
		printf("pthread_mutex_lock error %s\n", strerror(errno));
		return -1;
	}
#endif
	ai_stream.aec = NULL;
	ai_stream.aec_size = 0;
	ai_stream.timestamp = tv;
	if ((pch->ref_en)||(pch->ai_internel_aec)) {
		if (NULL == ref) {
			IMP_LOG_ERR(TAG, "ai ref enabled but ref buf is NULL\n");
			return -1;
		}
		ai_stream.aec = ref;
		ai_stream.aec_size = aisize / pdev->audioIAttr.chnCnt;
	} else {
		ai_stream.aec = NULL;
	}
#if 0
	ret = pthread_mutex_unlock(&pdev->mutex_dev);
	if (ret != 0) {
		printf("pthread_mutex_unlock error %s\n", strerror(errno));
		return -1;
	}
#endif
	ai_stream.size = aisize;
	ret = ioctl(pdev->aifd, AMIC_AI_GET_STREAM, &ai_stream);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "ai AMIC_AI_GET_STREAM error %s\n", strerror(errno));
		return -1;
	}
	//printf("### buf = %p, aisize = %d, refsize = %d\n", buf, aisize, refsize);
	//printf("ai_stream.timestamp.tv_sec = %ld,ai_stream.timestamp.tv_usec = %ld\n", ai_stream.timestamp->tv_sec,ai_stream.timestamp->tv_usec);
	retsize = ai_stream.size;
	return retsize;
}

static inline int _ai_aec_lock(AudioInChn *pch)
{
	if(NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ai_aec);
}

static inline int _ai_aec_unlock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_unlock(&pch->mutex_ai_aec);
}

static inline int _ai_agc_lock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ai_agc);
}

static inline int _ai_agc_unlock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_unlock(&pch->mutex_ai_agc);
}

static inline int _ai_hpf_lock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ai_hpf);

}

static inline int _ai_hpf_unlock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_unlock(&pch->mutex_ai_hpf);

}

static inline int _ai_ns_lock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ai_ns);
}

static inline int _ai_ns_unlock(AudioInChn *pch)
{
	if (NULL == pch)
	return -1;

	return pthread_mutex_unlock(&pch->mutex_ai_ns);
}

static inline int _ai_hs_lock(AudioInChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ai_hs);
}

static inline int _ai_hs_unlock(AudioInChn *pch)
{
	if (NULL == pch)
	return -1;

	return pthread_mutex_unlock(&pch->mutex_ai_hs);
}

static inline int _ai_drc_lock(AudioInChn *pch)
{
   if (NULL == pch)
       return -1;

   return pthread_mutex_lock(&pch->mutex_ai_drc);
}

static inline int _ai_drc_unlock(AudioInChn *pch)
{
   if(NULL == pch)
       return -1;

   return pthread_mutex_unlock(&pch->mutex_ai_drc);
}

static int _ai_init_audioProcess_mutex(AudioInChn *pch)  //initial mutex about audioProcess(agc/ns/hpf/aec)
{
	if (NULL == pch) {
		IMP_LOG_ERR(TAG,"fun:%s\n ,AudioInChn pch is empty\n",__func__);
		return -1;
	}

	int ret = 0;

	if (pthread_mutex_init(&pch->mutex_ai_agc,NULL) != 0)
		ret = -1;
	if (pthread_mutex_init(&pch->mutex_ai_ns,NULL) != 0)
		ret = -1;
	if (pthread_mutex_init(&pch->mutex_ai_hpf,NULL) != 0)
		ret = -1;
	if (pthread_mutex_init(&pch->mutex_ai_aec,NULL) != 0)
		ret = -1;
	if (pthread_mutex_init(&pch->mutex_ai_drc,NULL) != 0)
		ret = -1;

	return ret;
}

static int  _ai_deinit_audioProcess_mutex(AudioInChn *pch) //destroy mutex about audioProcess(agc/ns/hpf/aec)
{
	if (NULL == pch) {
		IMP_LOG_ERR(TAG,"fun:%s,AudioInChn pch is empty.\n",__func__);
		return -1;
	}

	int ret = 0;

	if (pthread_mutex_destroy(&pch->mutex_ai_agc) != 0)
		ret = -1;
	if (pthread_mutex_destroy(&pch->mutex_ai_ns) != 0)
		ret = -1;
	if (pthread_mutex_destroy(&pch->mutex_ai_hpf) != 0)
		ret = -1;
	if (pthread_mutex_destroy(&pch->mutex_ai_aec) != 0)
		ret = -1;
	if (pthread_mutex_destroy(&pch->mutex_ai_drc) != 0)
		ret = -1;

	return ret;

}

int _ai_thread_post(AudioInDev *pdev)
{
	int ret = -1;
	CHECK_RET(pthread_cond_signal(&pdev->cond_dev));
	return 0;
}

int _ai_thread_wait(AudioInDev *pdev)
{
	struct timeval now;
	struct timespec timeout;
	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if(pthread_cond_timedwait(&pdev->cond_dev, &pdev->mutex_dev, &timeout) == ETIMEDOUT) {
		//IMP_LOG_WARN(TAG, "_ai_thread_wait timeout\n");
		;
	}
	return 0;
}

static int _ai_chn_disable_wait(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = 0;
	struct timeval now;
	struct timespec timeout;

	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
#if 1
	ret = pthread_kill(pdev->aiProcess, 0);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "disalbe chn ai thread has exit!\n");
		pch->enable = 0;
		return 0;
	}
#endif
	pch->enable = 0;
	pch->ch_disable_done = 0;
	while(0 == pch->ch_disable_done)
		if(pthread_cond_timedwait(&pch->cond_ai_ch_disable, &pdev->mutex_dev, &timeout) == ETIMEDOUT)
			IMP_LOG_WARN(TAG, "_ai_chn_disable_wait timeout\n");
	pch->ch_disable_done = 0;
	return 0;
}

static int _ai_chn_disable_post(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = -1;
	pch->ch_disable_done = 1;
	CHECK_RET(pthread_cond_signal(&pch->cond_ai_ch_disable));
	return 0;
}

static int _ai_chn_disable_ref_wait(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = 0;
	struct timeval now;
	struct timespec timeout;

	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
#if 1
	ret = pthread_kill(pdev->aiProcess, 0);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "disable ref ai thread has exit!\n");
		pch->enable = 0;
		return 0;
	}
#endif
	pch->ref_en = 0;
	pch->ch_disable_ref_done = 0;
	while(0 == pch->ch_disable_ref_done)
		if(pthread_cond_timedwait(&pch->cond_ai_ch_disable_ref, &pdev->mutex_dev, &timeout) == ETIMEDOUT)
			IMP_LOG_WARN(TAG, "_ai_chn_disable_wait timeout\n");
	pch->ch_disable_ref_done = 0;
	return 0;
}
static int _ai_chn_disable_ref_post(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = -1;
	pch->ch_disable_ref_done = 1;
	CHECK_RET(pthread_cond_signal(&pch->cond_ai_ch_disable_ref));
	return 0;
}

int _ai_chn_lock(AudioInChn *pch)
{
	int ret = -1;
	CHECK_RET(pthread_mutex_lock(&pch->mutex_chn));
	return 0;
}
int _ai_chn_unlock(AudioInChn *pch)
{
	int ret = -1;
	CHECK_RET(pthread_mutex_unlock(&pch->mutex_chn));
	return 0;
}

int _ai_polling_frame_wait(AudioInChn *pch)
{
	void *ainode = NULL;
	struct timeval now;
	struct timespec timeout;
	_ai_chn_lock(pch);
	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	while (NULL == (ainode = audio_buf_try_get_node(pch->ai_buf, AUDIO_BUF_FULL)))
		if (pthread_cond_timedwait(&pch->cond_ai_ch_polling, &pch->mutex_chn, &timeout) == ETIMEDOUT){
			IMP_LOG_WARN(TAG, "_ai_polling_frame_wait timeout\n");
			_ai_chn_unlock(pch);
			return -1;
		}
	_ai_chn_unlock(pch);
	 return 0;
}

static int _ai_Ns_Process(NsHandle_t* h, short* spframe, short* outframe, int size)
{
	int i,j,k;
	float *sp = h->sp;
	float *out = h->out;
	const float * const *p = (const float*const*)&sp;
	float * const *q = &out;
	k = size / (h->cell_num*2);
	if (k == 0)
		return -1;
	for (i = 0; i < k; i++) {
		for (j = 0; j < h->cell_num; j++) {
			sp[j] = spframe[j+(i*h->cell_num)];
		}
	memset(out, 0, h->cell_num*sizeof(float));
	fun_ai_ns_process(h->handle,p,1,q);
		for (j = 0; j < h->cell_num; j++) {
			outframe[j+(i*h->cell_num)] = out[j];
		}
	}

	return 0;
}

static int _ai_Agc_Process(void *agcInst, const int16_t *in_near, int16_t samples,
	                         int16_t *out, int32_t inMicLevel,
	                         int32_t *outMicLevel, int16_t echo,
	                         uint8_t *saturationWarning, int size)
{
	int ret = -1;
	int k, i = size / (samples*2);
	/* printf("size = %d, samples = %d, i = %d\n", size, samples, i); */
	const int16_t *p = in_near;
	int16_t *q = out;

	for(k = 0; k < i; k++) {
		const int16_t *ptmp = p + samples*k;
		int16_t *qtmp = q + samples*k;
		const int16_t *const*pp = &ptmp;
		int16_t *const*qq = &qtmp;
		ret = fun_ai_agc_process(agcInst,pp,1,samples,qq,inMicLevel,outMicLevel,echo,saturationWarning);
		if (ret != 0) {
			printf("%s, line %d : agc proccess error\n", __func__, __LINE__);
			return -1;
		}
	}

	return 0;
}

int _ai_Drc_Process(DrcHandle_t* h, short* spframe, short* outframe, int size)
{
   int i,j,k;
   float *sp = h->sp;
   float *out = h->out;
   const float *const *p = (const float* const*)&sp;
   float *const *q = &out;

   k = size/(h->cell_num*sizeof(short));
   //printf("k = %d\n",k);
// static int count = 0;
   if (k == 0)
       return -1;
   for (i = 0;i < k;i++){
//     count++;
       for(j = 0;j < h->cell_num;j++){
           sp[j] = spframe[j+(i*h->cell_num)];
       }
       memset(out,0,h->cell_num*sizeof(float));
       fun_ai_drc_process(h->handle,p,q);
       for(j = 0;j < h->cell_num;j++){
           outframe[j+(i*h->cell_num)] = out[j];
       }
   }
   //printf("count = %d\n",count);
   return 0;
}

static int _set_aiDrcEnableFlag(int audioDevId,int aiChn)
{
   AudioInChn *pch = NULL;

   if (audioDevId > MAX_AI_DEV) {
       IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
       return -1;
   }

   if (!aiDev[audioDevId].aiEnable) {
       IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
       return 0;
   }

   if (aiChn > MAX_AI_CHN) {
       IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
       return -1;
   }

   pch = &aiDev[audioDevId].audioInChn[aiChn];
   pch->ai_drc_enable_suppression = 1;

   return 0;
}

static int _reset_aiDrcEnableFlag(int audioDevId,int aiChn)
{
    AudioInChn *pch = NULL;

   if (audioDevId > MAX_AI_DEV) {
       IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
       return -1;
   }

   if (!aiDev[audioDevId].aiEnable) {
       IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
       return -1;
   }

   if (aiChn > MAX_AI_CHN) {
       IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
       return -1;
   }

   pch = &aiDev[audioDevId].audioInChn[aiChn];
   pch->ai_drc_enable_suppression = 0;

   return 0;

}

int _ai_InitializeFilter(void * filter, int sample_rate_hz,int cofrequency)
{
	FilterState* hpf = (FilterState *)filter;
	if (hpf == NULL) {
		printf("hpf is NULL\n");
		return -1;
	}

	if(cofrequency != 0){
		Hpf_gen_filter_coefficients(AiCoefficients, sample_rate_hz, cofrequency);
		hpf->ba = AiCoefficients;
	}else{
		if (sample_rate_hz == kSampleRate8kHz) {
			hpf->ba = kFilterCoefficients8kHz;
		} else {
			hpf->ba = kFilterCoefficients;
		}
	}
	fun_ai_hpf_create(hpf->x,hpf->y,0,0,2,4);

	return 0;
}

int _ai_HPF_Filter(void * filter, short* data, int size)
{
	int ret = -1;
	char *p = (char *)data;
	int i = 0, k = 0, samples = 0;
	k = size / 160;
	samples = size / 2 / k;
	/*printf("%s: size = %d, samples = %d, k = %d\n", __func__, size, samples, k);*/

	for (i = 0; i < k; i++) {
		ret = fun_ai_hpf_process(filter,(short*)(p+160*i),samples);
		if (ret != 0) {
			printf("%s: Filter error\n", __func__);
			return -1;
		}
	}
	return 0;
}

static int _set_aiAgcEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_agc_enable_suppression = 1;

	return 0;

}

static int _reset_aiAgcEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_agc_enable_suppression = 0;

	return 0;

}

static int _set_aiNsEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_ns_enable_suppression = 1;

	return 0;

}

static int _reset_aiNsEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
    }

	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_ns_enable_suppression = 0;

	return 0;

}

static int _set_aiHpfEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_hpf_enable_suppression = 1;

	return 0;
}

static int _set_aiHpfCutoffFrequency(int audioDevId,int aiChn,int cofrequency)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->hpf_co_frequency = cofrequency;

	return 0;
}

static int _get_aiHpfCutoffFrequency(int audioDevId,int aiChn)
{
	AudioInChn *pch = &aiDev[audioDevId].audioInChn[aiChn];
	return pch->hpf_co_frequency;
}

static int _reset_aiHpfEnableFlag(int audioDevId,int aiChn)
{
	AudioInChn *pch = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
	    return -1;
	}

	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->ai_hpf_enable_suppression = 0;

	return 0;
}

static uint64_t _ai_get_time_stamp()
{
	return IMP_System_GetTimeStamp();
}

static void *_ai_record_thread(void *argv) {
	int ret = -1;
	int size = 0;
	int outMicLevel = 0;
	unsigned char saturationWarning = 0;
	char thread_name[64];

	AudioInDev *pdev = argv;
	AudioInChn *pch = NULL;
	void *ainode = NULL;
	struct ai_data *ad = NULL;
	char *ref = NULL;
	short *aec_data = NULL;
	short *pdata = NULL;
	int aecChn = 0;
	int i = 0;
	struct timeval tv;

	if (NULL == argv) {
		printf("err: argv == NULL\n");
		goto err_param;
	}

	sprintf(thread_name, "ai-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	while (1) {
		int dev_en = 0;
		int ch_en = 0;
		int wait = 0;
		/* now only support channel 0 */
		ret = pthread_mutex_lock(&pdev->mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		pch = &pdev->audioInChn[0];
		do {
			dev_en = pdev->aiEnable;
			ch_en = pch->enable;
			if ((1 == ch_en)&&((0 == pch->ref_en) || (0 == pch->ai_internel_aec))) {
				_ai_chn_disable_ref_post(pdev, pch);
			}
			wait = (1 == dev_en) && (0 == ch_en);
			if (1 == wait) {
				_ai_chn_disable_post(pdev, pch);
				_ai_thread_wait(pdev);
			}
		} while (1 == wait);
		if (0 == dev_en) {
			ret = pthread_mutex_unlock(&pdev->mutex_dev);
			if (0 != ret) {
				printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
				break;
			}
			break;
		}
		ret = pthread_mutex_unlock(&pdev->mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		_ai_chn_lock(pch);
		ainode = audio_buf_get_node(pch->ai_buf, AUDIO_BUF_EMPTY);
		if (NULL == ainode) {
		//	printf("warn: ai thread can not get empty buf\n");
			ainode = audio_buf_get_node(pch->ai_buf, AUDIO_BUF_FULL);
			if (NULL == ainode) {
				printf("err: ai thread can not get full buf\n");
				break;
			}
		}
		ad = audio_buf_node_get_data(ainode);
		size = pdev->audioIAttr.numPerFrm*sizeof(short);
		if (((1 == pch->ref_en)||(1 == pch->ai_internel_aec)) && (NULL != pch->ref_buf))
			ref = pch->ref_buf+audio_buf_node_index(ainode)*size;
		else
			ref = NULL;
		_ai_chn_unlock(pch);

		ret = __ai_dev_read(pdev, pch, (char *)ad->data, ref, pdev->audioIAttr.chnCnt*sizeof(short)*pdev->audioIAttr.numPerFrm, &tv);
		if (ret < 0) {
			printf("err: __ai_dev_read  ret= %d\n", ret);
			break;
		}
        ad->raw_data = NULL;
        if (pch->get_raw_en && pch->get_raw_switch) {
            ad->raw_data = (char*)ad->data + sizeof(short)*pdev->audioIAttr.numPerFrm;
            memcpy((char*)ad->raw_data, (char*)ad->data, sizeof(short)*pdev->audioIAttr.numPerFrm);
        }
		ad->timestamp = (int64_t)((int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec);
		if(ad->timestamp == 0){
			_ai_chn_lock(pch);
			audio_buf_put_node(pch->ai_buf, ainode, AUDIO_BUF_EMPTY);
			_ai_chn_unlock(pch);
			continue;
		}
		ad->timestamp -= (timestamp_base + 20000);
		ad->node = ainode;
		ad->size = ret;
		if(ai_frm_num > 0){
			dbg_ai_get_frm((uint32_t *)ad->data,ret,WRITE_SOURCE);
		}
		ret = -1;
		/* ai process */
		if ((pch->ai_internel_aec)&&(NULL != ref)) {
			//do aec related ai process
			if(pdev->audioIAttr.chnCnt == 2){
				aec_data = (short *)pch->aec_buf;
				pdata = (short *)ad->data;
				aecChn = pch->audioIChnParam.aecChn;
				for(i = 0; i < pdev->audioIAttr.numPerFrm; i++){
					aec_data[i] = pdata[i * pdev->audioIAttr.chnCnt + aecChn];
				}
			}else{
				aec_data = (short *)(ad->data);
			}

			_ai_aec_lock(pch);
			struct aec_process_param p = {ref, (char *)aec_data, (char *)aec_data, ad->size/pdev->audioIAttr.chnCnt};
			ret = fun_ai_aec_process(pch->aec_handle, &p);
			if(ret != 0){
				IMP_LOG_ERR(TAG,"ai aec process error.\n");
			}
			_ai_aec_unlock(pch);
			aec_data = (short*)aec_data;

			if (pch->ai_drc_enable_suppression) {
				_ai_drc_lock(pch);
				if (handle_ai_drc.handle != NULL) {
					ret = _ai_Drc_Process(&handle_ai_drc,(short*)ad->data,(short*)ad->data, ad->size/pdev->audioIAttr.chnCnt);
					if (ret != 0) {
						IMP_LOG_ERR(TAG,"IngenicDrc_Process error.\n");
						_ai_drc_unlock(pch);
						return ret;
					}
				}
				_ai_drc_unlock(pch);
			}

			if(pdev->audioIAttr.chnCnt == 2){
				for(i = 0; i < pdev->audioIAttr.numPerFrm; i++){
					pdata[i * pdev->audioIAttr.chnCnt + aecChn] = aec_data[i];
				}
			}
		} else{
			if(pch->ai_ns_enable_suppression) {
				_ai_ns_lock(pch);
				ret = _ai_Ns_Process(&handle_ns,(short*)ad->data,(short*)ad->data,ad->size);
				if(ret != 0){
					IMP_LOG_ERR(TAG,"_ai_Ns_Process error.\n");
					_ai_ns_unlock(pch);
					return NULL;
				}
				_ai_ns_unlock(pch);
			}
			if(pch->ai_agc_enable_suppression) {
				_ai_agc_lock(pch);
				ret = _ai_Agc_Process(handle_ai_agc, (short *)ad->data,
						agc_sample_i,(short *)ad->data,127,&outMicLevel, 0,&saturationWarning,ad->size);
				if(ret != 0) {
					IMP_LOG_ERR(TAG,"_ai_Agc_Process error.\n");
					_ai_agc_unlock(pch);
					return NULL;
				}
				_ai_agc_unlock(pch);
			}
		
			if (pch->ai_drc_enable_suppression) {
				_ai_drc_lock(pch);
				if (handle_ai_drc.handle != NULL) {
					ret = _ai_Drc_Process(&handle_ai_drc,(short*)ad->data,(short*)ad->data, ad->size);
					if (ret != 0) {
						IMP_LOG_ERR(TAG,"IngenicDrc_Process error.\n");
						_ai_drc_unlock(pch);
						return ret;
					}
				}
				_ai_drc_unlock(pch);
			}
			
			if(pch->ai_hpf_enable_suppression) {
				_ai_hpf_lock(pch);
				ret = _ai_HPF_Filter(&handle_ai_hpf,(short*)ad->data,ad->size);
				if(ret != 0){
					IMP_LOG_ERR(TAG,"_ai_HPF_Filter error.\n");
					_ai_hpf_unlock(pch);
					return NULL;
				}
				_ai_hpf_unlock(pch);
			}
		}

		if (pch->ai_hs_enable_suppression && g_audio_dev_info.ao_en) {
			_ai_hs_lock(pch);

			ret = fun_ai_hs_process(handle_ai_hs, ad->data, ad->data, ad->size);
			// printf("ad->size = %d\n", ad->size);
			if(ret != 0){
				IMP_LOG_ERR(TAG,"ai hs process error.\n");
			}

			_ai_hs_unlock(pch);
		}

		/* set vol */
		_audio_set_volume((char *)ad->data, (char *)ad->data, ad->size, 16, pch->vol.vol);
        if (pch->get_raw_en && pch->get_raw_switch)
            _audio_set_volume((char*)ad->raw_data, (char*)ad->raw_data, ad->size, 16, pch->vol.vol);
		_ai_chn_lock(pch);
		audio_buf_put_node(pch->ai_buf, ainode, AUDIO_BUF_FULL);
		if(ai_frm_num > 0){
			dbg_ai_get_frm((uint32_t *)ad->data,ad->size,WRITE_TARGET);
			ai_frm_num --;
			if(ai_frm_num <= 0){
				ai_frm_num  = 0;
			}
		}
		pthread_cond_signal(&pch->cond_ai_ch_polling);
		_ai_chn_unlock(pch);
	}
	ai_frm_num  = 0;
	IMP_LOG_INFO(TAG, "ai record thread exit\n");
	pthread_exit(0);
	return 0;

err_param:
	ai_frm_num  = 0;
	pthread_exit(0);
	IMP_LOG_ERR(TAG, "err: ai record thread exit\n");
	return 0;
}

int _ai_get_buf_size(void)
{
	AudioInDev *pdev = NULL;
	int size = 0;
	pdev = &aiDev[MAX_AI_DEV];
	size = pdev->audioIAttr.numPerFrm*sizeof(short);
	return size;
}

int _ai_ref_enable(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = -1;
	int depth = 0;
	int size = 0;
	int offset = 0;

	ret = __ai_dev_enable_aec(pdev,&offset);
	if (0 != ret) {
		printf("err: __ai_dev_enable_aec\n");
		return ret;
	}
	depth = pch->audioIChnParam.usrFrmDepth;
	size = pdev->audioIAttr.numPerFrm*sizeof(short);
	_ai_chn_lock(pch);
	pch->ref_buf = malloc(depth*size);
	if (NULL == pch->ref_buf) {
		IMP_LOG_ERR(TAG, "%s %d Ai ref buf malloc error.\n", __func__, __LINE__);
		_ai_chn_unlock(pch);
		return -1;
	}
	memset(pch->ref_buf, 0, depth*size);
	_ai_chn_unlock(pch);
	return 0;
}

int _ai_ref_disable(AudioInDev *pdev, AudioInChn *pch)
{
	int ret = -1;
	ret = __ai_dev_disable_aec(pdev);
	if (0 != ret) {
		printf("err: __ai_dev_disable_aec\n");
		return ret;
	}
	return 0;
}

static int _ai_check_user_attr(AudioInDev *pdev ,IMPAudioIOAttr *pattr)
{
	if (NULL == pdev||NULL == pattr) {
		IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
		return -1;
	}

	pdev->user_numperfrm = pattr->numPerFrm;  /*set user numperfrm*/

	pdev->getFrameWay = GET_FRAME_DEFAULT_WAY;

	if (pattr->numPerFrm < (pattr->samplerate/100)*4) {  /*small than 40ms*/
		pattr->numPerFrm = (pattr->samplerate/100)*4;//??????????????????????????????????
		pdev->getFrameWay = GET_FRAME_USER_WAY;
	}
	return 0;

}

static int _ai_set_tmpbuf_size(AudioInDev *pdev, AudioInChn *pch)
{
	if (NULL == pdev||NULL == pch) {
		IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
		return -1;
	}

	int i = pdev->user_numperfrm/(pdev->audioIAttr.samplerate/100);/*user_numperfrm:10ms;20ms;30ms*/
	switch (i) {
		case 1:
			pch->ai_tmp_size = (pdev->audioIAttr.samplerate/100)*3*sizeof(short);/*10ms:need 30ms size tmp buf*/
			break;
		case 2:
			pch->ai_tmp_size = (pdev->audioIAttr.samplerate/100)*2*sizeof(short);/*20ms:need 20ms size tmp buf*/
			break;
		case 3:
			pch->ai_tmp_size = (pdev->audioIAttr.samplerate/100)*6*sizeof(short);/*30ms:need 60ms size tmp buf*/
			break;
		default:{
			IMP_LOG_ERR(TAG,"fun:%s,error case.\n",__func__);
			return -1;
		}
	}
	return 0;
}

static void *_ai_alloc_tmpbuf(int size)
{
	void *pBuf = NULL;
	if (size <= 0) {
		IMP_LOG_ERR(TAG,"fun:%s,parameter size is smaller than zero.\n",__func__);
		return NULL;
	}

	pBuf = malloc(size);
	if (NULL == pBuf) {
		IMP_LOG_ERR(TAG,"fun:%s,malloc buf failed\n",__func__);
		return NULL;
	}

	memset(pBuf, 0x0, size);
	return pBuf;
}

static void _ai_free_tmpbuf(void *pBuf)
{
	if (NULL == pBuf)
		return;

	free(pBuf);
	return;
}

static int _raw_set_tmpbuf_size(AudioInDev *pdev, AudioInChn *pch)
{
   if (NULL == pdev||NULL == pch) {
       IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
       return -1;
   }

   int i = pdev->user_numperfrm/(pdev->audioIAttr.samplerate/100);/*user_numperfrm:10ms;20ms;30ms*/
   switch (i) {
       case 1:
           pch->raw_tmp_size = (pdev->audioIAttr.samplerate/100)*3*sizeof(short);/*10ms:need 30ms size tmp buf*/
           break;
       case 2:
           pch->raw_tmp_size = (pdev->audioIAttr.samplerate/100)*2*sizeof(short);/*20ms:need 20ms size tmp buf*/
           break;
       case 3:
           pch->raw_tmp_size = (pdev->audioIAttr.samplerate/100)*6*sizeof(short);/*30ms:need 60ms size tmp buf*/
           break;
       default:{
           IMP_LOG_ERR(TAG,"fun:%s,error case.\n",__func__);
           return -1;
       }
   }

   return 0;
}

static void *_raw_alloc_tmpbuf(int size)
{
   void *pBuf = NULL;
   if (size <= 0) {
       IMP_LOG_ERR(TAG,"fun:%s,parameter size is smaller than zero.\n",__func__);
       return NULL;
   }

   pBuf = malloc(size);
   if (NULL == pBuf) {
       IMP_LOG_ERR(TAG,"fun:%s,malloc buf failed\n",__func__);
       return NULL;
   }

   memset(pBuf, 0x0, size);
   return pBuf;
}

static void _raw_free_tmpbuf(void *pBuf)
{
   if (NULL == pBuf)
       return;

   free(pBuf);
   return;
}

static int _default_way_get_frame(AudioInDev *pdev , AudioInChn *pch ,void *ainode, struct ai_data *ad,IMPAudioFrame *frm, IMPAudioFrame *raw_frm, IMPBlock block)
{
	if (NULL == pdev||NULL == pch||NULL == frm) {
		IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
		return -1;
	}

	do {
		_ai_chn_lock(pch);
		ainode = audio_buf_get_node(pch->ai_buf,AUDIO_BUF_FULL);
		_ai_chn_unlock(pch);

		if (NULL != ainode) {
			ad = audio_buf_node_get_data(ainode);
			pch->audioFrame.virAddr = (uint32_t *)ad->data;
			pch->audioFrame.len = ad->size;
			pch->audioFrame.timeStamp = ad->timestamp;
			pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
			pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;
			memcpy(frm , &pch->audioFrame, sizeof(IMPAudioFrame));
            if (NULL != raw_frm) {
                pch->raw_audioFrame.virAddr = (uint32_t*)ad->raw_data;
                pch->raw_audioFrame.len = (ad->raw_data && pch->get_raw_switch)?ad->size:0;
                pch->raw_audioFrame.timeStamp = ad->timestamp;
                pch->raw_audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
                pch->raw_audioFrame.soundmode = pdev->audioIAttr.soundmode;
                memcpy(raw_frm, &pch->raw_audioFrame, sizeof(IMPAudioFrame));
            }
			return 0;
		}

		if(block == BLOCK)
			_ai_polling_frame_wait(pch);

	} while (block == BLOCK);

	return -1;
}


static int _default_way_release_frame(AudioInChn *pch, struct ai_data *ad, IMPAudioFrame *frm)
{
	if (NULL == pch||NULL == frm) {
		IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
		return -1;
	}

	_ai_chn_lock(pch);
	ad = container_of((void *)frm->virAddr, struct ai_data, data);
	audio_buf_put_node(pch->ai_buf, ad->node, AUDIO_BUF_EMPTY);
	pch->audioFrame.virAddr = NULL;
	pch->audioFrame.len = 0;
	pch->audioFrame.timeStamp = 0;
	pch->audioFrame.seq++;
	_ai_chn_unlock(pch);

	return 0;
}

static int _default_way_get_frameAndref(AudioInDev *pdev , AudioInChn *pch ,void *ainode,
			 struct ai_data *ad, IMPAudioFrame *frm, IMPAudioFrame *ref, IMPBlock block)
{
	if (NULL == pdev||NULL == pch||NULL == frm||NULL == ref) {
		IMP_LOG_ERR(TAG,"fun:%s,parameters is invalid.\n",__func__);
		return -1;
	}

	do {
		_ai_chn_lock(pch);
		ainode = audio_buf_get_node(pch->ai_buf, AUDIO_BUF_FULL);
		_ai_chn_unlock(pch);
		if (NULL != ainode) {
			int size = pdev->audioIAttr.numPerFrm*sizeof(short);
			ad = audio_buf_node_get_data(ainode);
			pch->audioFrame.virAddr = (uint32_t *)ad->data;
			pch->audioFrame.len = ad->size;
			/* timestamp */
			pch->audioFrame.timeStamp = ad->timestamp;
			pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
			pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;
			memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
			/* ref */
			ref->virAddr = pch->ref_buf+audio_buf_node_index(ainode)*size;
			ref->len = ad->size;
			ref->timeStamp = ad->timestamp;
			ref->bitwidth = pdev->audioIAttr.bitwidth;
			ref->soundmode = pdev->audioIAttr.soundmode;
			return 0;
		}

		if (block == BLOCK)
			_ai_polling_frame_wait(pch);
	} while (block == BLOCK);

	return -1;
}


int IMP_AI_IMPDBG_Init(void)
{
     int ret = 0;

     ret =  func_init();
     ret += dsys_func_share_mem_register(FUNC_MODULE_AI_DEV, DBG_Ai_INFO, "impdbg_ai_dev_info", impdbg_ai_dev_info);
     ret += dsys_func_share_mem_register(FUNC_MODULE_AI_DEV, DBG_Ai_GET_FRM, "impdbg_ai_get_frm", impdbg_ai_get_frm);

	 return ret;
}

int IMP_AI_IMPDBG_DeInit(void)
{
     int ret = 0;

     ret =  func_deinit();
     ret += dsys_func_unregister(FUNC_MODULE_AI_DEV, DBG_Ai_INFO);
     ret += dsys_func_unregister(FUNC_MODULE_AI_DEV, DBG_Ai_GET_FRM);

	 return ret;
}

int IMP_AI_EnableAgc(IMPAudioIOAttr *attr, IMPAudioAgcConfig agcConfig)
{
	int ret = -1;
	AgcConfig config;
	config.targetLevelDbfs = agcConfig.TargetLevelDbfs;
	config.compressionGaindB = agcConfig.CompressionGaindB;
	config.limiterEnable = 1;

	IMP_LOG_INFO(TAG, "AI AGC ENABLE: targetLevelDbfs = %d, compressionGaindB = %d, limiterEnable =%d\n",
					            config.targetLevelDbfs, config.compressionGaindB, config.limiterEnable);
	/* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/*  ret = WebRtcAgc_Version(version, 20); */
	/*  IMP_LOG_INFO(TAG, "AGC version is: %s\n", version); */
	/*  free(version); */
	/* } */
	 ret = init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
	 if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n", __func__);
		return -1;
	 }

	 FUN_TYPE agcfun[5] = {{FUN_AGC_CREATE_NAME,(void**)&fun_ai_agc_create},
						  {FUN_AGC_SET_CONFIG_NAME,(void**)&fun_ai_agc_set_config},
						  {FUN_AGC_GET_CONFIG_NAME,(void**)&fun_ai_agc_get_config},
						  {FUN_AGC_PROCESS_NAME,(void**)&fun_ai_agc_process},
						  {FUN_AGC_FREE_NAME,(void**)&fun_ai_agc_free}
						 };

	ret = get_fun_address(g_lib_audioProcess_handle,agcfun,5);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		return -1;
	}
	handle_ai_agc = fun_ai_agc_create();
	if (NULL == handle_ai_agc) {
		IMP_LOG_ERR(TAG, "fun:%s,agc creat error.\n",__func__);
		return -1;
	}

	if (8000 == attr->samplerate) {
		agc_sample_i = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		agc_sample_i = 160;
	} else {
		IMP_LOG_ERR(TAG, "Agc do not supoort samplerate: %d\n", attr->samplerate);
		return -1;
	}
	ret = fun_ai_agc_set_config(handle_ai_agc,0,255,agc_mode,attr->samplerate,config);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"fun:%s,agc set config error.\n",__func__);
		return -1;
	}
	ret = _set_aiAgcEnableFlag(1,0);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"_set_aiAgcEnableFlag failed\n");
		return -1;
	}

	return 0;
}

int IMP_AI_DisableAgc(void)
{
	IMP_LOG_INFO(TAG, "AI AGC DISABLE\n");
	if(handle_ai_agc)
		fun_ai_agc_free(handle_ai_agc);

	_reset_aiAgcEnableFlag(1,0);
	fun_ai_agc_create = NULL;
	fun_ai_agc_set_config = NULL;
	fun_ai_agc_get_config = NULL;
	fun_ai_agc_process = NULL;
	fun_ai_agc_free = NULL;
	return 0;
}

int IMP_AI_EnableNs(IMPAudioIOAttr *attr, int mode)
{
	int ret = -1;
	if ((mode > 3) || (mode < 0)) {
		IMP_LOG_WARN(TAG, "mode too big, should be 0 <= mode <= 3. use default mode 3.\n");
		mode = 3;
	}
	IMP_LOG_INFO(TAG, "AI NS ENABLE: mode = %d\n", mode);

	AudioInChn *pch = NULL;
	pch = &aiDev[1].audioInChn[0];
	if (1 == pch->ai_ns_enable_suppression) {
		IMP_LOG_ERR(TAG,"AI NS already enable\n");
		return 0;
	}
	_ai_ns_lock(pch);

	 /* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/*  ret = WebRtcNs_get_version(version, 20); */
	/*  IMP_LOG_INFO(TAG, "NS version is: %s\n", version); */
	/*  free(version); */
	/* } */

	ret = init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n", __func__);
		return -1;
	}

	FUN_TYPE nsfun[5] = {{FUN_NS_CREATE_NAME,(void**)&fun_ai_ns_create},
				{FUN_NS_SET_CONFIG_NAME,(void**)&fun_ai_ns_set_config},
				{FUN_NS_GET_CONFIG_NAME,(void**)&fun_ai_ns_get_config},
				{FUN_NS_PROCESS_NAME,(void**)&fun_ai_ns_process},
				{FUN_NS_FREE_NAME,(void**)&fun_ai_ns_free}};

	ret = get_fun_address(g_lib_audioProcess_handle,nsfun,5);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		return -1;
	}
	handle_ns.handle = fun_ai_ns_create();
	if (NULL == handle_ns.handle) {
		IMP_LOG_ERR(TAG,"fun:%s create ns handle failed\n",__func__);
		return -1;
	}
	if (8000 == attr->samplerate) {
		handle_ns.cell_num = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		handle_ns.cell_num = 160;
	} else {
		IMP_LOG_ERR(TAG, "NS do not supoort samplerate: %d\n", attr->samplerate);
		goto err_ns_err_samplerate;
	}

	ret = fun_ai_ns_set_config(handle_ns.handle,attr->samplerate,mode);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s ns set config failed\n",__func__);
		return -1;
	}
	handle_ns.sp = malloc(handle_ns.cell_num*sizeof(float));
	if (NULL == handle_ns.sp) {
		IMP_LOG_ERR(TAG, "Ns_sp_malloc error.\n");
		goto err_ns_sp_malloc;
	}
	memset(handle_ns.sp, 0, handle_ns.cell_num*sizeof(float));
	handle_ns.out = malloc(handle_ns.cell_num*sizeof(float));
	if (NULL == handle_ns.out) {
		IMP_LOG_ERR(TAG, "Ns_out_malloc error.\n");
		goto err_ns_out_malloc;
	}
	memset(handle_ns.out, 0, handle_ns.cell_num*sizeof(float));

	ret = _set_aiNsEnableFlag(1,0);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"_set_aiNsEnableFlag failed\n");
		return -1;
	}

	 _ai_ns_unlock(pch);

	return 0;

err_ns_out_malloc:
err_ns_sp_malloc:
	if(handle_ns.handle){
		fun_ai_ns_free(handle_ns.handle);
		handle_ns.handle = NULL;
	}
	if (handle_ns.sp) {
		free(handle_ns.sp);
		handle_ns.sp = NULL;
	}
	if (handle_ns.out) {
		free(handle_ns.out);
		handle_ns.out = NULL;
	}
err_ns_err_samplerate:
	pthread_mutex_unlock(&ns_mutex);
	return -1;
}

int IMP_AI_DisableNs(void)
{
	IMP_LOG_INFO(TAG, "AI NS DISABLE\n");

	AudioInChn *pch = NULL;
	pch = &aiDev[1].audioInChn[0];
	_ai_ns_lock(pch);

	if (NULL == handle_ns.handle) {
		IMP_LOG_WARN(TAG, "AI NS is not enabled\n");
		_ai_ns_unlock(pch);
		return 0;
	}

	_reset_aiNsEnableFlag(1,0);
	if(handle_ns.handle){
		fun_ai_ns_free(handle_ns.handle);
		handle_ns.handle = NULL;
	}
	if (handle_ns.sp) {
		free(handle_ns.sp);
		handle_ns.sp = NULL;
	}
	if (handle_ns.out) {
		free(handle_ns.out);
		handle_ns.out = NULL;
	}

	_ai_ns_unlock(pch);

	fun_ai_ns_create = NULL;
	fun_ai_ns_set_config = NULL;
	fun_ai_ns_get_config = NULL;
	fun_ai_ns_process = NULL;
	fun_ai_ns_free = NULL;

	return 0;
}

int IMP_AI_EnableHpf(IMPAudioIOAttr *attr)
{
	IMP_LOG_INFO(TAG,"AI HPF Enable\n");
	int ret = -1;
	int cofrequency = _get_aiHpfCutoffFrequency(1,0);
	if(cofrequency < 0 || cofrequency > attr->samplerate){
		IMP_LOG_ERR(TAG,"HPF cut-off frequency is illegal.\n");
		return -1;
	}
	ret = init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n", __func__);
		return -1;
	}

	FUN_TYPE hpffun[3] = {{FUN_HPF_CREATE_NAME,(void**)&fun_ai_hpf_create},
		{FUN_HPF_PROCESS_NAME,(void**)&fun_ai_hpf_process},
		{FUN_HPF_FREE_NAME,(void**)&fun_ai_hpf_free}};

	ret = get_fun_address(g_lib_audioProcess_handle,hpffun,3);
	if(ret != 0){
		IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		return -1;
	}
	char * version = (char *)malloc(sizeof(char) * 32);
	if (version != NULL) {
		ret = Hpf_Version(version, 32);
			IMP_LOG_INFO(TAG, "HPF version is: %s\n", version);
			free(version);
	}
	ret = _ai_InitializeFilter(&handle_ai_hpf, attr->samplerate,cofrequency);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "HPF InitializeFilter error.\n");
		return -1;
	}
	ret = _set_aiHpfEnableFlag(1,0);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"_set_aiHpfEnableFlag failed\n");
		return -1;
	}
	return 0;
}

int IMP_AI_SetHpfCoFrequency(int cofrequency)
{
	int ret = -1;
	ret = _set_aiHpfCutoffFrequency(1,0,cofrequency);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"_set_aiHpfCutoffFrequency failed\n");
		return -1;
	}
	return 0;
}

int IMP_AI_DisableHpf(void)
{
	IMP_LOG_INFO(TAG, "AI HPF DISABLE\n");

	_reset_aiHpfEnableFlag(1,0);

	fun_ai_hpf_create = NULL;
	fun_ai_hpf_process = NULL;
	fun_ai_hpf_free = NULL;

	return 0;
}

int IMP_AI_EnableHs()
{
	int ret = -1;
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG, "AI HS ENABLE\n");

	pch = &aiDev[1].audioInChn[0];
	if (1 == pch->ai_hs_enable_suppression) {
		IMP_LOG_ERR(TAG,"AI HS already enable\n");
		return 0;
	}

	_ai_hs_lock(pch);

	ret = init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n", __func__);
		return -1;
	}

	FUN_TYPE hsfun[3] = {{FUN_HS_CREATE_NAME,(void**)&fun_ai_hs_create},
						  {FUN_HS_PROCESS_NAME,(void**)&fun_ai_hs_process},
						  {FUN_HS_FREE_NAME,(void**)&fun_ai_hs_free}};

	ret = get_fun_address(g_lib_audioProcess_handle, hsfun, 3);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "%s: get fun address failed\n", __func__);
		return -1;
	}

	handle_ai_hs = fun_ai_hs_create();
	if (NULL == handle_ai_hs) {
		IMP_LOG_ERR(TAG, "%s, hs creat error!\n",__func__);
		return -1;
	}

	pch->ai_hs_enable_suppression = 1;

	 _ai_hs_unlock(pch);

	return 0;
}

int IMP_AI_DisableHs()
{
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG, "AI HS DISABLE.\n");

	pch = &aiDev[1].audioInChn[0];
	_ai_hs_lock(pch);

	if (NULL == handle_ai_hs) {
		IMP_LOG_WARN(TAG, "AI HS is not enabled\n");
		_ai_hs_unlock(pch);
		return 0;
	}

	fun_ai_hs_free(handle_ai_hs);
	handle_ai_hs = NULL;
	pch->ai_hs_enable_suppression = 0;

	fun_ai_hs_create = NULL;
	fun_ai_hs_process = NULL;
	fun_ai_hs_free = NULL;

	_ai_hs_unlock(pch);

	return 0;
}

int IMP_AI_EnableDrc(IMPAudioIOAttr *attr, IMPAudioDrcConfig drcConfig)
{
   IMP_LOG_INFO(TAG,"AI Enable DRC\n");
   int ret = -1;

   ret =  init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
   if (ret != 0) {
       IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n",__func__);
       return -1;
   }


   FUN_TYPE drcfun[4] = {{FUN_DRC_CREATE_NAME,(void**)&fun_ai_drc_create},
                       {FUN_DRC_SET_CONFIG_NAME,(void**)&fun_ai_drc_set_config},
                       {FUN_DRC_PROCESS_NAME,(void**)&fun_ai_drc_process},
                       {FUN_DRC_FREE_NAME,(void**)&fun_ai_drc_free}};


   ret = get_fun_address(g_lib_audioProcess_handle,drcfun,4);
   if (ret != 0){
       IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
       return -1;
   }

   handle_ai_drc.handle = fun_ai_drc_create();
   if (NULL == handle_ai_drc.handle) {
       IMP_LOG_ERR(TAG, "fun:%s,drc create error.\n",__func__);
       return -1;
   }
	// handle_drc.cell_num must be 10ms data.
	switch(attr->samplerate){
	case 8000:
		handle_ai_drc.cell_num = 80;
		break;
	case 16000:
		handle_ai_drc.cell_num = 160;
		break;
	default:
		printf("Ns do not support samplerate: %d\n",attr->samplerate);
		return NULL;
		break;
	}
	ret = fun_ai_drc_set_config(handle_ai_drc.handle, attr->samplerate, drcConfig.threshold1, drcConfig.ratio, drcConfig.threshold2, drcConfig.mode, drcConfig.supPoint, drcConfig.supExp);
	   if (ret != 0) {
       IMP_LOG_ERR(TAG, "fun:%s agc set config error\n",__func__);
       return -1;
   }

   // mem for float data
   handle_ai_drc.sp = (float*)malloc(handle_ai_drc.cell_num*sizeof(float));
   if(NULL == handle_ai_drc.sp){
       printf("Drc_sp_malloc error.\n");
       return NULL;
   }
   memset(handle_ai_drc.sp,0,handle_ai_drc.cell_num*sizeof(float));
   handle_ai_drc.out = (float*)malloc(handle_ai_drc.cell_num*sizeof(float));
   if(NULL == handle_ai_drc.out) {
       printf("Drc_out_malloc error\n");
       return NULL;
   }
   memset(handle_ai_drc.out,0,handle_ai_drc.cell_num*sizeof(float));

   ret = _set_aiDrcEnableFlag(1,0);
   if (ret != 0) {
       IMP_LOG_ERR(TAG,"_set_aiDrcEnableFlag failed\n");
       return -1;
   }

   return 0;
}

int IMP_AI_DisableDrc(void)
{
   IMP_LOG_INFO(TAG, "AI DRC DISABLE\n");
   AudioInChn *pch = NULL;
   pch = &aiDev[1].audioInChn[0];
   _ai_drc_lock(pch);

   if (handle_ai_drc.handle == NULL) {
       _ai_drc_unlock(pch);
       IMP_LOG_WARN(TAG, "AI DRC NOT ENABLED\n");
       return 0;
   }

   _reset_aiDrcEnableFlag(1,0);
   if(handle_ai_drc.handle)
   {
       fun_ai_drc_free(handle_ai_drc.handle);
       handle_ai_drc.handle = NULL;
   }
   if (handle_ai_drc.sp) {
       free(handle_ai_drc.sp);
       handle_ai_drc.sp = NULL;
   }
   if (handle_ai_drc.out) {
       free(handle_ai_drc.out);
       handle_ai_drc.out = NULL;
   }
   _ai_drc_unlock(pch);
   fun_ai_drc_free(handle_ai_drc.handle);
   handle_ai_drc.handle = NULL;
   fun_ai_drc_create = NULL;
   fun_ai_drc_set_config = NULL;
   fun_ai_drc_process = NULL;
   fun_ai_drc_free = NULL;

    return 0;
}

int IMP_AI_SetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (attr->chnCnt > MAX_AI_TRACK) {
		IMP_LOG_ERR(TAG, "set ai pub attr error, invalid chnCnt.\n");
		return -1;
	}
	if (attr->soundmode == AUDIO_SOUND_MODE_STEREO){
		if (attr->chnCnt != 2) {
			IMP_LOG_ERR(TAG, "Stereo mode, analog mic chnCnt = 2.\n");
			return -1;
		}
	}else{
		if (attr->chnCnt != 1) {
			IMP_LOG_ERR(TAG, "Mono mode, analog mic chnCnt = 1.\n");
			return -1;
		}
	}
	pdev = &aiDev[audioDevId];
	if ((((1000 * attr->numPerFrm) / attr->samplerate) % 10) != 0) {
		IMP_LOG_ERR(TAG, "ai samples(numPerFrm) must be an integer number of 10ms (10ms * n).\n");
		return -1;
	}

	ret = _ai_check_user_attr(pdev , attr); /*check user set attr*/
	if (ret != 0)
		IMP_LOG_ERR(TAG,"line:%d,check user attr failed\n",__LINE__);

	memcpy(&pdev->audioIAttr, attr, sizeof(IMPAudioIOAttr));

	return 0;
}

int IMP_AI_GetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	AudioInDev *pdev = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	memcpy(attr, &aiDev[audioDevId].audioIAttr, sizeof(IMPAudioIOAttr));
	attr->numPerFrm = pdev->user_numperfrm;

	return 0;
}

int IMP_AI_Enable(int audioDevId)
{
	int ret = -1;
	AudioInDev *pdev = NULL;

	IMP_LOG_INFO(TAG, "AI Enable: %d\n", audioDevId);
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (aiDev[audioDevId].aiEnable == 1) {
		IMP_LOG_ERR(TAG, "ai device is already enabled.\n");
		return 0;
	}
	pdev = &aiDev[audioDevId];
	if (pdev->audioIAttr.bitwidth != AUDIO_BIT_WIDTH_16) {
		IMP_LOG_ERR(TAG, "ai in bit width error, only support AUDIO_BIT_WIDTH_16\n");
		return -1;
	}
	ret = __ai_dev_init(pdev);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "__ai_dev_init error %s\n", strerror(errno));
		return ret;
	}
	pthread_mutex_init(&pdev->mutex_dev, NULL);
	pthread_cond_init(&pdev->cond_dev, NULL);
	pdev->aiEnable = 1;
	g_audio_dev_info.ai_en = 1;
	ret = pthread_create(&pdev->aiProcess, NULL, _ai_record_thread, (void *)pdev);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "ai record thread create error %s\n", strerror(errno));
		return ret;
	}
	return 0;
}

int IMP_AI_Disable(int audioDevId)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	IMP_LOG_INFO(TAG, "AI Disable: %d\n", audioDevId);
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if(aiDev[audioDevId].aiEnable == 0) {
		IMP_LOG_ERR(TAG, "ai device is already Disabled.\n");
		return 0;
	}
	pdev = &aiDev[audioDevId];
	CHECK_RET(pthread_mutex_lock(&pdev->mutex_dev));
	pdev->aiEnable = 0;
	g_audio_dev_info.ai_en = 0;
	_ai_thread_post(pdev);
	CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));
	pthread_join(pdev->aiProcess, NULL);
	__ai_dev_deinit(pdev);

	ret = pthread_mutex_destroy(&pdev->mutex_dev);
	if (0 != ret) {
		printf("err: destroy mutex_dev\n");
		return ret;
	}

	ret = pthread_cond_destroy(&pdev->cond_dev);
	if (0 != ret) {
		printf("err: destroy cond_dev_thread\n");
		return ret;
	}

	if (!g_audio_dev_info.ao_en)
		deinit_audioProcess_library();
	return 0;
}

int IMP_AI_EnableChn(int audioDevId, int aiChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	int depth = 0;
	int size = 0;
	IMP_LOG_INFO(TAG, "AI Enable Chn: %d-%d\n", audioDevId, aiChn);
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (!pdev->aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return 0;
	}
	if (pch->enable == 1) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai channel is already enabled.\n", __func__, __LINE__);
		return 0;
	}
	depth = pch->audioIChnParam.usrFrmDepth;
	//size = pdev->audioIAttr.numPerFrm * sizeof(short) * pdev->audioIAttr.chnCnt + sizeof(struct ai_data);
    if (pch->get_raw_en)
        size = pdev->audioIAttr.numPerFrm*sizeof(short)*pdev->audioIAttr.chnCnt*2 + sizeof(struct ai_data);
    else
        size = pdev->audioIAttr.numPerFrm*sizeof(short)*pdev->audioIAttr.chnCnt + sizeof(struct ai_data);
	if (depth < 2 || depth > pdev->audioIAttr.frmNum) {
		IMP_LOG_ERR(TAG, "The channel parameters are not set.\n");
		return -1;
	}

	pthread_mutex_init(&pch->mutex_chn, NULL);
	pthread_cond_init(&pch->cond_ai_ch_disable, NULL);
	pthread_cond_init(&pch->cond_ai_ch_disable_ref, NULL);

	_ai_chn_lock(pch);
	pch->ai_buf = audio_buf_alloc(depth, size, 0);
	if (NULL == pch->ai_buf) {
		IMP_LOG_ERR(TAG, "audio_buf_alloc depth = %d, size = %d\n", depth, size);
		return -1;
	}

	if (GET_FRAME_USER_WAY == pdev->getFrameWay) {
		ret = _ai_set_tmpbuf_size(pdev, pch); /*set value of ai_tmp_size*/
		if (ret != 0) {
			IMP_LOG_ERR(TAG,"line:%d,ai set tmpbuf size failed\n",__LINE__);
			return -1;
		}

		pch->ai_tmp_buf = _ai_alloc_tmpbuf(pch->ai_tmp_size);
		if (NULL == pch->ai_tmp_buf) {
			IMP_LOG_ERR(TAG,"line:%d,ai record alloc buf failed.\n",__LINE__);
			return -1;
		}
        if (pch->get_raw_en) {
            ret = _raw_set_tmpbuf_size(pdev, pch);
            if (ret != 0) {
                IMP_LOG_ERR(TAG,"line:%d,ai raw set tmpbuf size failed\n",__LINE__);
                return -1;
            }
            pch->raw_tmp_buf = _raw_alloc_tmpbuf(pch->raw_tmp_size);
            if (NULL == pch->raw_tmp_buf){
                IMP_LOG_ERR(TAG,"line:%d,raw record alloc buf failed.\n",__LINE__);
                return -1;
            }
			pch->raw_copybuf = pch->raw_tmp_buf;
        }
		pch->copybuf = pch->ai_tmp_buf;
		pch->user_frmlen = (pdev->user_numperfrm)*sizeof(short);
		pch->copybytes = 0;
	}

	_ai_chn_unlock(pch);

	/* set default ai vol. */
	pch->ai_vol_coef = pow(10, ((60.0 * 0.5) - 30.0) / 20.0);
	pch->vol.vol = pch->ai_vol_coef;
	pch->ai_vol = 60;

	/*initial mutex of audioProcess*/
	ret = _ai_init_audioProcess_mutex(pch);
	if(ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s\n ai init audioProcess mutex failed\n", __func__);
		return -1;
	}
	ret = IMP_AI_IMPDBG_Init();
	if(0 != ret) {
		printf("ai impdbg init fail!!!\n");
	}
	CHECK_RET(pthread_mutex_lock(&pdev->mutex_dev));
	pch->aec_handle = NULL;
	pch->hpf_co_frequency = 0;
	pch->enable = 1;
	_ai_thread_post(pdev);
	CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));
	IMP_LOG_INFO(TAG, "EXIT AI Enable Chn: %d-%d\n", audioDevId, aiChn);

	return 0;
}

int IMP_AI_DisableChn(int audioDevId, int aiChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	IMP_LOG_INFO(TAG, "AI Disable Chn: %d-%d\n", audioDevId, aiChn);
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	if (aiDev[audioDevId].audioInChn[aiChn].enable == 0) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai channel is already disabled.\n", __func__, __LINE__);
		return 0;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	CHECK_RET(pthread_mutex_lock(&pdev->mutex_dev));
	_ai_chn_disable_wait(pdev, pch);
	audio_buf_free(pch->ai_buf);
	CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));
#if 0
	ret = pthread_mutex_destroy(&pch->mutex_chn);
	if (0 != ret) {
		printf("err: destroy mutex_chn\n");
		return ret;
	}
	ret = pthread_cond_destroy(&pch->cond_ai_ch_disable);
	if (0 != ret) {
		printf("err: destroy cond_ai_ch_disable\n");
		return ret;
	}
	ret = pthread_cond_destroy(&pch->cond_ai_ch_disable_ref);
	if (0 != ret) {
		printf("err: destroy cond_ai_ch_disable_ref\n");
		return ret;
	}
#endif
	ret = _ai_deinit_audioProcess_mutex(pch);
	if (0 != ret) {
		printf("err:deinit_audioProcess-mutex.\n");
		return ret;
	}
	ret = IMP_AI_IMPDBG_DeInit();
	if(0 != ret) {
		printf("ai impdbg deinit fail!!!\n");
	}

	_ai_free_tmpbuf(pch->ai_tmp_buf);
	pch->ai_tmp_buf = NULL;
    _raw_free_tmpbuf(pch->raw_tmp_buf);
    pch->raw_tmp_buf = NULL;
    pch->get_raw_en = 0;
	IMP_LOG_INFO(TAG, "EXIT AI Disable Chn: %d-%d\n", audioDevId, aiChn);
	return 0;
}

int IMP_AI_EnableGetRaw(int audioDevId, int aiChn)
{
   int ret = -1;
   AudioInDev *pdev = NULL;
   AudioInChn *pch = NULL;

   IMP_LOG_INFO(TAG, "AI Disable Chn: %d-%d\n", audioDevId, aiChn);

   if (audioDevId > MAX_AI_DEV) {
       IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
       return -1;
   }
   if (aiChn > MAX_AI_CHN) {
       IMP_LOG_ERR(TAG, "Invalid ai channel..\n");
       return -1;
   }

   pdev = &aiDev[audioDevId];
   pch = &aiDev[audioDevId].audioInChn[aiChn];
   pch->get_raw_en = 1;
   pch->get_raw_switch = 1;

   return 0;
}

int IMP_AI_GetRawSwitch(int audioDevId, int aiChn, int on_off)
{
   int ret = -1;
   AudioInDev *pdev = NULL;
   AudioInChn *pch = NULL;

   IMP_LOG_INFO(TAG, "AI Disable Chn: %d-%d\n", audioDevId, aiChn);

   if (audioDevId > MAX_AI_DEV) {
       IMP_LOG_ERR(TAG, "Invalaid ai device ID.\n");
       return -1;
   }
   if (aiChn > MAX_AI_CHN) {
       IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
       return -1;
   }

   pdev = &aiDev[audioDevId];
   pch = &aiDev[audioDevId].audioInChn[aiChn];
   pch->get_raw_switch = on_off;

   return 0;
}

int IMP_AI_GetFrame(int audioDevId, int aiChn, IMPAudioFrame *frm, IMPBlock block)
{
	int ret = -1;
	static int64_t temp_timestamp = 0;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	void *ainode = NULL;
	struct ai_data *ad = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (!aiDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (GET_FRAME_DEFAULT_WAY == pdev->getFrameWay) {
        ret = _default_way_get_frame(pdev , pch ,ainode, ad, frm, NULL, block);
		return ret;
	}

	do {

		if (pch->copybytes < pch->user_frmlen) {
			_ai_chn_lock(pch);
			ainode = audio_buf_get_node(pch->ai_buf,AUDIO_BUF_FULL);
			_ai_chn_unlock(pch);

			if (NULL != ainode) {
				ad = audio_buf_node_get_data(ainode);

				if (temp_timestamp == 0)
					temp_timestamp = ad->timestamp;

				/*timestamp*/
				temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
				pch->audioFrame.timeStamp = temp_timestamp;
				pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
				pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;

				if (0 == pch->copybytes) {
					pch->getNodeFlag = GET_NODE_FLAG_ONE;

					pch->audioFrame.virAddr = (uint32_t *)ad->data;
					pch->audioFrame.len = pch->user_frmlen;
					memcpy(frm , &pch->audioFrame, sizeof(IMPAudioFrame));

					memcpy(pch->ai_tmp_buf, (char*)ad->data + pch->user_frmlen, ad->size - pch->user_frmlen);/*copy remain ad audio data to tmpbuf*/
					pch->copybuf = pch->ai_tmp_buf;
					pch->copybytes = ad->size - pch->user_frmlen;

					return 0;

				} else if (pch->copybytes > 0 && pch->copybytes < pch->user_frmlen) {

					pch->getNodeFlag = GET_NODE_FLAG_TWO;
					pch->ai_data_address = (void*)ad;

					if (pch->copybuf != pch->ai_tmp_buf) {      /*judge pch->copybuf is equal to ai_tmp_buf*/
						memmove(pch->ai_tmp_buf, pch->copybuf, pch->copybytes);
						pch->copybuf = pch->ai_tmp_buf;
					}
					memcpy(pch->ai_tmp_buf + pch->copybytes, ad->data, ad->size);
					pch->copybytes += ad->size;

					pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
					pch->audioFrame.len = pch->user_frmlen;
					memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
					pch->copybuf += pch->user_frmlen;
					pch->copybytes -= pch->user_frmlen;

					return 0;
				}
			}
		} else if (pch->copybytes >= pch->user_frmlen) {

				pch->getNodeFlag = GET_NODE_FLAG_ZERO;

				/*timestamp*/
				temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
				pch->audioFrame.timeStamp = temp_timestamp;
				pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
				pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;

				pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
				pch->audioFrame.len = pch->user_frmlen;

				memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
				pch->copybuf += pch->user_frmlen;
				pch->copybytes -= pch->user_frmlen;

				return 0;
		}
		if (block == BLOCK)
			_ai_polling_frame_wait(pch);

	} while (block == BLOCK);

	return -1;
}

int IMP_AI_GetFrameAndRaw(int audioDevId, int aiChn, IMPAudioFrame *frm, IMPAudioFrame *raw_frm, IMPBlock block)
{
    int ret = -1;
    static int64_t temp_timestamp = 0;
    AudioInDev *pdev = NULL;
    AudioInChn *pch = NULL;
    void *ainode = NULL;
    struct ai_data *ad = NULL;
    if (audioDevId > MAX_AI_DEV) {
        IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
        return -1;
    }
    if (aiChn > MAX_AI_CHN) {
        IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
        return -1;
    }
    pdev = &aiDev[audioDevId];
    pch = &aiDev[audioDevId].audioInChn[aiChn];
    if (!aiDev[audioDevId].aiEnable) {
        IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
        return -1;
    }

    if (!aiDev[audioDevId].audioInChn[aiChn].enable) {
        IMP_LOG_ERR(TAG, "%s %d ai Channel is no enabled.\n", __func__, __LINE__);
        return -1;
    }

    if (GET_FRAME_DEFAULT_WAY == pdev->getFrameWay) {
                ret = _default_way_get_frame(pdev , pch ,ainode, ad, frm, raw_frm, block);
        return ret;
    }
   do {

   if (pch->copybytes < pch->user_frmlen) {
       _ai_chn_lock(pch);
       ainode = audio_buf_get_node(pch->ai_buf,AUDIO_BUF_FULL);
       _ai_chn_unlock(pch);

       if (NULL != ainode) {
           ad = audio_buf_node_get_data(ainode);

           if (temp_timestamp == 0)
               temp_timestamp = ad->timestamp;

           /*timestamp*/
           temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
           pch->audioFrame.timeStamp = temp_timestamp;
           pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
           pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;

			if (NULL != raw_frm) {
				pch->raw_audioFrame.timeStamp = temp_timestamp;
                pch->raw_audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
                pch->raw_audioFrame.soundmode = pdev->audioIAttr.soundmode;
			}

           if (0 == pch->copybytes) {
               pch->getNodeFlag = GET_NODE_FLAG_ONE;

               pch->audioFrame.virAddr = (uint32_t *)ad->data;
               pch->audioFrame.len = pch->user_frmlen;
               memcpy(frm , &pch->audioFrame, sizeof(IMPAudioFrame));
				if (NULL != raw_frm) {
					pch->raw_audioFrame.virAddr = (uint32_t*)ad->raw_data;
                	pch->raw_audioFrame.len = (ad->raw_data && pch->get_raw_switch)?pch->user_frmlen:0;
					memcpy(raw_frm, &pch->raw_audioFrame, sizeof(IMPAudioFrame));

					memcpy(pch->raw_tmp_buf, (char*)ad->raw_data + pch->user_frmlen, ad->size - pch->user_frmlen);/*copy remain ad audio data to tmpbuf*/
               		pch->raw_copybuf = pch->raw_tmp_buf;
					pch->raw_copybytes = ad->size - pch->user_frmlen;
				}
               memcpy(pch->ai_tmp_buf, (char*)ad->data + pch->user_frmlen, ad->size - pch->user_frmlen);/*copy remain ad audio data to tmpbuf*/
               pch->copybuf = pch->ai_tmp_buf;
               pch->copybytes = ad->size - pch->user_frmlen;

               return 0;

           } else if (pch->copybytes > 0 && pch->copybytes < pch->user_frmlen) {
                   pch->getNodeFlag = GET_NODE_FLAG_TWO;
                   pch->ai_data_address = (void*)ad;

                   if (pch->copybuf != pch->ai_tmp_buf) {      /*judge pch->copybuf is equal to ai_tmp_buf*/
                       memmove(pch->ai_tmp_buf, pch->copybuf, pch->copybytes);
                       pch->copybuf = pch->ai_tmp_buf;
						if (NULL != raw_frm) {
							memmove(pch->raw_tmp_buf, pch->raw_copybuf, pch->raw_copybytes);
                       		pch->raw_copybuf = pch->raw_tmp_buf;
						}
                   }
					if (NULL != raw_frm) {
						memcpy(pch->raw_tmp_buf + pch->raw_copybytes, ad->raw_data, ad->size);
						pch->raw_copybytes += ad->size;

						pch->raw_audioFrame.virAddr = (uint32_t *)pch->raw_copybuf;
						pch->raw_audioFrame.len = pch->user_frmlen;
						memcpy(raw_frm, &pch->raw_audioFrame, sizeof(IMPAudioFrame));
						pch->raw_copybuf += pch->user_frmlen;
						pch->raw_copybytes -= pch->user_frmlen;
					}
                   memcpy(pch->ai_tmp_buf + pch->copybytes, ad->data, ad->size);
                   pch->copybytes += ad->size;

                   pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
                   pch->audioFrame.len = pch->user_frmlen;
                   memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
                   pch->copybuf += pch->user_frmlen;
                   pch->copybytes -= pch->user_frmlen;

                   return 0;
               }
           }
       } else if (pch->copybytes >= pch->user_frmlen) {
               pch->getNodeFlag = GET_NODE_FLAG_ZERO;

               /*timestamp*/
               temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
               pch->audioFrame.timeStamp = temp_timestamp;
               pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
               pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;

               pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
               pch->audioFrame.len = pch->user_frmlen;

               memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
               pch->copybuf += pch->user_frmlen;
               pch->copybytes -= pch->user_frmlen;

				if (NULL != raw_frm) {
					pch->raw_audioFrame.timeStamp = temp_timestamp;
					pch->raw_audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
					pch->raw_audioFrame.soundmode = pdev->audioIAttr.soundmode;

					pch->raw_audioFrame.virAddr = (uint32_t *)pch->raw_copybuf;
					pch->raw_audioFrame.len = pch->user_frmlen;

					memcpy(raw_frm, &pch->raw_audioFrame, sizeof(IMPAudioFrame));
					pch->raw_copybuf += pch->user_frmlen;
					pch->raw_copybytes -= pch->user_frmlen;
				}

               return 0;
       }
       if (block == BLOCK)
           _ai_polling_frame_wait(pch);

   } while (block == BLOCK);

   return -1;
}

int IMP_AI_GetFrameAndRef(int audioDevId, int aiChn, IMPAudioFrame *frm, IMPAudioFrame *ref, IMPBlock block)
{
	int ret = -1;
	static int64_t temp_timestamp = 0;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	void *ainode = NULL;
	int size = 0;
	struct ai_data *ad = NULL;

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (!aiDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (GET_FRAME_DEFAULT_WAY == pdev->getFrameWay) {
		ret = _default_way_get_frameAndref(pdev, pch, ainode, ad, frm, ref, block);
		return ret;
	}

	size = pdev->audioIAttr.numPerFrm*sizeof(short);
	do {

		if ((pch->copybytes < pch->user_frmlen) && (pch->copyRefbytes < pch->user_ref_frmlen)) {

			_ai_chn_lock(pch);
			ainode = audio_buf_get_node(pch->ai_buf, AUDIO_BUF_FULL);
			_ai_chn_unlock(pch);

			if (NULL != ainode) {
				ad = audio_buf_node_get_data(ainode);

				if (temp_timestamp == 0)
					temp_timestamp = ad->timestamp;

				/*timestamp*/
				temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
				pch->audioFrame.timeStamp = temp_timestamp;
				pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
				pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;
				ref->timeStamp = temp_timestamp;
				ref->bitwidth = pdev->audioIAttr.bitwidth;
				ref->soundmode = pdev->audioIAttr.soundmode;

				if ((0 == pch->copybytes)&&(0 == pch->copyRefbytes)) {

					pch->getNodeFlag = GET_NODE_FLAG_ONE;

					pch->audioFrame.virAddr = (uint32_t *)ad->data;
					pch->audioFrame.len = pch->user_frmlen;
					memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
					memcpy(pch->ai_tmp_buf,(char*)ad->data + pch->user_frmlen, ad->size - pch->user_frmlen);
					pch->copybuf = pch->ai_tmp_buf;
					pch->copybytes = ad->size - pch->user_frmlen;

					/*ref*/
					ref->virAddr = pch->ref_buf+audio_buf_node_index(ainode)*size;
					ref->len = pch->user_ref_frmlen;

					memcpy(pch->ai_tmp_refBuf, (char*)pch->ref_buf+audio_buf_node_index(ainode)*size, ad->size-pch->user_ref_frmlen);
					pch->copyRefbuf = pch->ai_tmp_refBuf;
					pch->copyRefbytes = ad->size - pch->user_ref_frmlen;

					return 0;

				} else if ((pch->copybytes > 0) && (pch->copybytes < pch->user_frmlen)
							&&(pch->copyRefbytes > 0)&& (pch->copyRefbytes < pch->user_ref_frmlen)) {

					pch->getNodeFlag = GET_NODE_FLAG_TWO;
					pch->ai_data_address = (void*)ad;

					if (pch->copybuf != pch->ai_tmp_buf) {
						memmove(pch->ai_tmp_buf, pch->copybuf, pch->copybytes);
						pch->copybuf = pch->ai_tmp_buf;
					}

					if (pch->copyRefbuf != pch->ai_tmp_refBuf) {
						memmove(pch->ai_tmp_refBuf, pch->copyRefbuf, pch->copyRefbytes);
						pch->copyRefbuf = pch->ai_tmp_refBuf;
					}

					memcpy(pch->ai_tmp_buf + pch->copybytes, ad->data, ad->size);
					pch->copybytes += ad->size;

					memcpy(pch->ai_tmp_refBuf + pch->copyRefbytes, (char*)pch->ref_buf+audio_buf_node_index(ainode)*size, ad->size);
					pch->copyRefbytes += ad->size;

					pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
					pch->audioFrame.len = pch->user_frmlen;

					memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
					pch->copybuf += pch->user_frmlen;
					pch->copybytes -= pch->user_frmlen;

					/*ref*/
					ref->virAddr = (uint32_t *)pch->copyRefbuf;
					ref->len = pch->user_ref_frmlen;

					pch->copyRefbuf += pch->user_ref_frmlen;
					pch->copyRefbytes -= pch->user_ref_frmlen;

					return 0;
				}
			}
		} else if ((pch->copybytes >= pch->user_frmlen)&&(pch->copyRefbytes >= pch->user_ref_frmlen)) {
			pch->getNodeFlag = GET_NODE_FLAG_ZERO;

			/*timeStamp*/
			temp_timestamp += pdev->user_numperfrm / (pdev->audioIAttr.samplerate/1000) * 1000;
			pch->audioFrame.timeStamp = temp_timestamp;
			pch->audioFrame.bitwidth = pdev->audioIAttr.bitwidth;
			pch->audioFrame.soundmode = pdev->audioIAttr.soundmode;

			pch->audioFrame.virAddr = (uint32_t *)pch->copybuf;
			pch->audioFrame.len = pch->user_frmlen;

			memcpy(frm, &pch->audioFrame, sizeof(IMPAudioFrame));
			pch->copybuf += pch->user_frmlen;
			pch->copybytes -= pch->user_frmlen;

			/*ref*/
			ref->virAddr = (uint32_t*)pch->copyRefbuf;
			ref->len = pch->user_ref_frmlen;
			ref->timeStamp = temp_timestamp;
			ref->bitwidth = pdev->audioIAttr.bitwidth;
			ref->soundmode = pdev->audioIAttr.soundmode;

			pch->copyRefbuf += pch->user_ref_frmlen;
			pch->copyRefbytes -= pch->user_ref_frmlen;

			return 0;
		}

		if (block == BLOCK)
			_ai_polling_frame_wait(pch);

	} while (block == BLOCK);

	return -1;
}

int IMP_AI_EnableAecRefFrame(int audioDevId, int aiChn, int audioAoDevId, int aoChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG, "AI_EnableAecRefFrame:audioDevId = %d,aiChn = %d,audioAoDevId = %d,aoChn = %d\n",audioDevId,aiChn,audioDevId,aoChn);

	if(audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!pdev->aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!pch->enable) {
		IMP_LOG_ERR(TAG, "%s %d Ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	if (1 == pch->ai_internel_aec) {
		IMP_LOG_ERR(TAG, "%s %d Internal AEC has enabled. . AEC and Ref can not enable both.\n", __func__, __LINE__);
		return -1;
	}
	ret = _ai_ref_enable(pdev, pch);
	if(0 != ret) {
		IMP_LOG_ERR(TAG, "%s %d _ai_ref_enable error\n", __func__, __LINE__);
		return -1;
	}
	if (GET_FRAME_USER_WAY == pdev->getFrameWay) {
		ret = _ai_set_tmpbuf_size(pdev, pch); /*set value of ai_tmp_size*/
		if (ret != 0) {
			IMP_LOG_ERR(TAG,"line:%d,ai set tmpbuf size failed\n",__LINE__);
			return -1;
		}

		pch->ai_tmp_refBuf = _ai_alloc_tmpbuf(pch->ai_tmp_size);
		if (NULL == pch->ai_tmp_refBuf) {
			IMP_LOG_ERR(TAG,"line:%d,ai record alloc buf failed.\n",__LINE__);
			return -1;
		}
		pch->user_ref_frmlen = (pdev->user_numperfrm)*sizeof(short);
		pch->copyRefbuf = pch->ai_tmp_refBuf;
		pch->copyRefbytes = 0;
	}

	pch->ref_en = 1;
	return 0;
}

int IMP_AI_DisableAecRefFrame(int audioDevId, int aiChn, int audioAoDevId, int aoChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG,"AI_DisableAecRefFrame:audioDevId = %d,aiChn = %d,audioAoDevId = %d,aoChn = %d\n",audioDevId,
	aiChn,audioDevId,aoChn);

	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!pdev->aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!pch->enable) {
		IMP_LOG_ERR(TAG, "%s %d Ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	if (0 == pch->ref_en) {
		IMP_LOG_INFO(TAG, "%s %d ai Ref is not enabled.\n", __func__, __LINE__);
		return 0;
	}
	CHECK_RET(pthread_mutex_lock(&pdev->mutex_dev));
	ret = _ai_ref_disable(pdev, pch);
	if(0 != ret) {
		IMP_LOG_ERR(TAG, "%s %d _ai_ref_disable error\n", __func__, __LINE__);
		CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));
		return -1;
	}
	_ai_chn_disable_ref_wait(pdev, pch);
	if (NULL != pch->ref_buf)
		free(pch->ref_buf);
	pch->ref_buf = NULL;
	CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));

	_ai_free_tmpbuf(pch->ai_tmp_refBuf);
	pch->ai_tmp_refBuf = NULL;
	return 0;
}

int IMP_AI_PollingFrame(int audioDevId, int aiChn, unsigned int timeout_ms)
{
	AudioInChn *pch = NULL;
	int ret = -1;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!aiDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	ret = _ai_polling_frame_wait(pch);
	return ret;
}

int IMP_AI_ReleaseFrame(int audioDevId, int aiChn, IMPAudioFrame *frm)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	struct ai_data *ad = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];

	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] aio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (!aiDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (GET_FRAME_DEFAULT_WAY == pdev->getFrameWay) {
		ret = _default_way_release_frame(pch, ad, frm);
		return ret;
	}

	_ai_chn_lock(pch);
	if (GET_NODE_FLAG_ONE == pch->getNodeFlag) {

		ad = container_of((void *)frm->virAddr, struct ai_data, data);
		audio_buf_put_node(pch->ai_buf, ad->node, AUDIO_BUF_EMPTY);

	} else if (GET_NODE_FLAG_TWO == pch->getNodeFlag) {
		ad = (struct ai_data*)pch->ai_data_address;
		audio_buf_put_node(pch->ai_buf, ad->node, AUDIO_BUF_EMPTY);
	}

	pch->audioFrame.virAddr = NULL;
	pch->audioFrame.len = 0;
	pch->audioFrame.timeStamp = 0;
	pch->audioFrame.seq++;

	_ai_chn_unlock(pch);

	return 0;
}

int IMP_AI_SetChnParam(int audioDevId, int aiChn, IMPAudioIChnParam *chnParam)
{
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	if(chnParam->aecChn > AUDIO_AEC_CHANNEL_SECOND_RIGHT){
		IMP_LOG_ERR(TAG, "Invalid track channel,only left and right track\n");
		return -1;
	}

	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if( !aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (chnParam->usrFrmDepth < 2 || chnParam->usrFrmDepth > pdev->audioIAttr.frmNum) {
		IMP_LOG_ERR(TAG, "Invalid ai channel usrFrmDepth.\n");
		return -1;
	}
	pch->audioIChnParam.usrFrmDepth = chnParam->usrFrmDepth;
	pch->audioIChnParam.aecChn = chnParam->aecChn;
	return 0;
}

int IMP_AI_GetChnParam(int audioDevId, int aiChn, IMPAudioIChnParam *chnParam)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (!aiDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	chnParam->usrFrmDepth = pch->audioIChnParam.usrFrmDepth;
	return 0;
}

int IMP_AI_Set_WebrtcProfileIni_Path(char *path)
{
	if(NULL == path) {
		IMP_LOG_ERR(TAG, "Invalid path set,The default path will be used.\n");
	} else {
		if(0 == access(path, F_OK)){
			sprintf(pathbuff, "%s/%s", path, "webrtc_profile.ini");
		} else {
			IMP_LOG_ERR(TAG, "Invalid path set.\n");
			return -1;
		}
	}
	return 0;
}


int IMP_AI_EnableAec(int AiDevId, int aiChn, int AoDevId, int aoChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG,"AI_EnableAec:AiDevId = %d,aiChn = %d,AoDevId = %d,aoChn = %d\n",AiDevId,aiChn,
	AoDevId,aoChn);

	if(AiDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	if(!aiDev[AiDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!aiDev[AiDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	pdev = &aiDev[AiDevId];
	pch = &aiDev[AiDevId].audioInChn[aiChn];
	IMP_LOG_INFO(TAG, "AI AEC ENABLE\n");

	if (1 == pch->ref_en) {
		IMP_LOG_INFO(TAG, "%s %d ai Ref has enabled. AEC and Ref can not enable both.\n", __func__, __LINE__);
		return 0;
	}
	ret = init_audioProcess_library(LIB_AUDIOPROCESS_FILE_NAME);
	if(ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n", __func__);
		return -1;
	}
	FUN_TYPE aecfun[3] = {{FUN_AEC_CREATE_NAME,(void**)&fun_ai_aec_create},
						{FUN_AEC_PROCESS_NAME,(void**)&fun_ai_aec_process},
						{FUN_AEC_FREE_NAME,(void**)&fun_ai_aec_free}};

	ret = get_fun_address(g_lib_audioProcess_handle,aecfun,3);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		return -1;
	}
	_ai_aec_lock(pch);
	pch->aec_handle = fun_ai_aec_create(pdev->audioIAttr.samplerate, pathbuff);
	if (NULL == pch->aec_handle){
		IMP_LOG_ERR(TAG, "fun:%s,aec creat error.\n",__func__);
		_ai_aec_unlock(pch);
		return -1;
	}
	ret = _ai_ref_enable(pdev, pch);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "%s %d _ai_ref_enable error\n", __func__, __LINE__);
		return -1;
	}
	pch->aec_buf = (char *)malloc(pdev->audioIAttr.numPerFrm * sizeof(short));
	if(!pch->aec_buf){
		IMP_LOG_ERR(TAG, "%s %d aec buf malloc error\n", __func__, __LINE__);
		return -1;
	}
	pch->ai_internel_aec = 1;
	_ai_aec_unlock(pch);
	return 0;
}

int IMP_AI_DisableAec(int AiDevId, int aiChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;

	IMP_LOG_INFO(TAG,"AI_DisableAec:AiDevId = %d,aiChn = %d\n",AiDevId,aiChn);

	if (AiDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}

	if (!aiDev[AiDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ai device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (!aiDev[AiDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Ai Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	pdev = &aiDev[AiDevId];
	pch = &aiDev[AiDevId].audioInChn[aiChn];
	IMP_LOG_INFO(TAG, "AI AEC DISABLE\n");
	_ai_aec_lock(pch);
	if (0 == pch->ai_internel_aec) {
		IMP_LOG_INFO(TAG, "%s %d ai AEC is not enabled. \n", __func__, __LINE__);
		_ai_aec_unlock(pch);
		return 0;
	}
	_ai_aec_unlock(pch);

	_ai_aec_lock(pch);
	pch->ai_internel_aec = 0;
	_ai_aec_unlock(pch);
	ret = fun_ai_aec_free(pch->aec_handle);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "fun:%s,aec free error.\n",__func__);
		_ai_aec_unlock(pch);
		return -1;
	}
	pch->aec_handle = NULL;
	fun_ai_aec_create = NULL;
	fun_ai_aec_process = NULL;
	fun_ai_aec_free = NULL;
	CHECK_RET(pthread_mutex_lock(&pdev->mutex_dev));
	ret = _ai_ref_disable(pdev, pch);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "%s %d _ai_ref_disable error\n", __func__, __LINE__);
		return -1;
	}
	_ai_chn_disable_ref_wait(pdev, pch);
	if (NULL != pch->ref_buf)
		free(pch->ref_buf);
	if(NULL != pch->aec_buf)
		free(pch->aec_buf);
	pch->ref_buf = NULL;
	pch->aec_buf = NULL;
	CHECK_RET(pthread_mutex_unlock(&pdev->mutex_dev));
	return 0;
}

int IMP_AI_EnableAlgo(int audioDevId, int aiChn)
{

	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	IMPAudioAgcConfig agcConfig;
	int ns_level =0;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	//ns
	if(read_profile(pathbuff, "NS_AI","NS_enable", profile_buff) < 0){
		IMP_LOG_ERR(TAG,"%s: cannot find the value of NS_AI:SN_enable\n", __func__);
	}else if (!strcmp(profile_buff, "true")){
		if(read_profile(pathbuff, "NS_AI","set_level", profile_buff) < 0){
			IMP_LOG_ERR(TAG,"%s: cannot find the value of NS_AI:set_level,use defualt kHigh\n", __func__);
			ns_level = 2;
		}else if(!strcmp(profile_buff, "kLow")){
			ns_level = 0;
		}else if(!strcmp(profile_buff, "kModerate")){
			ns_level = 1;
		}else if(!strcmp(profile_buff, "kHigh")){
			ns_level = 2;
		}else if(!strcmp(profile_buff, "kVeryHigh")){
			ns_level = 3;
		}else{
			IMP_LOG_ERR(TAG,"%s: select NS_AI:level (KLow/kModerate/kHigh/kVeryHigh),use default kHigh \n", __func__);
			ns_level = 2;
		}
		ret = IMP_AI_EnableNs(&pdev->audioIAttr, ns_level);
		if(ret != 0){
			IMP_LOG_ERR(TAG,"%s: enable ns is fail \n", __func__);
		}
	}else {
		IMP_LOG_ERR(TAG,"%s: NS_AI is disable\n", __func__);
	}

	//agc
	if(read_profile(pathbuff, "AGC_AI","AGC_enable", profile_buff) < 0){
		IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AI:AGC_enable\n", __func__);
	}else if(!strcmp(profile_buff, "true")){
		if(read_profile(pathbuff, "AGC_AI","set_mode", profile_buff) < 0){
			IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AI:set_mode,use default kAdaptiveDigital\n", __func__);
			agc_mode = 2;
		}else if(!strcmp(profile_buff, "kAdaptiveAnalog")){
			agc_mode = 1;
		}else if(!strcmp(profile_buff, "kAdaptiveDigital")){
			agc_mode = 2;
		}else if(!strcmp(profile_buff, "kFixedDigital")){
			agc_mode = 3;
		}else{
			IMP_LOG_ERR(TAG,"%s: select AGC_AI:level (kFixedDigital/kAdaptiveAnalog/kAdaptiveAnalog).use defualt kAdaptiveDigital \n", __func__);
			agc_mode = 2;
		}
		if(read_profile(pathbuff, "AGC_AI","set_target_level_dbfs", profile_buff) < 0){
			IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AI:set_target_level_dbfs, use default 3\n", __func__);
			agcConfig.TargetLevelDbfs = 3;
		}else {
			agcConfig.TargetLevelDbfs = atoi(profile_buff);
		}
		if(read_profile(pathbuff, "AGC_AI","set_compression_gain_db", profile_buff) < 0){
			IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AI:set_compression_gain_db, use default 1\n", __func__);
			agcConfig.CompressionGaindB = 1;
		}else{
			agcConfig.CompressionGaindB = atoi(profile_buff);
		}
		ret = IMP_AI_EnableAgc(&pdev->audioIAttr, agcConfig);
		if(ret != 0){
			IMP_LOG_ERR(TAG,"%s: enable agc is fail \n", __func__);
		}

	}else{

		IMP_LOG_ERR(TAG,"%s: AGC_AI is disable\n", __func__);
	}
	//hpf
	if(read_profile(pathbuff, "HP_AI","HP_enable", profile_buff) < 0){
		IMP_LOG_ERR(TAG,"%s: cannot find the value of HP_AI:HP_enable\n", __func__);
	}else if(!strcmp(profile_buff, "true")){
		if(read_profile(pathbuff, "HP_AI","cofrequency", profile_buff) < 0){
			IMP_LOG_ERR(TAG,"%s: cannot find the value of HP_AI:cofrequency, use default 400\n", __func__);
			pch->hpf_co_frequency  = 400;
		}else{
			pch->hpf_co_frequency = atoi(profile_buff);
		}
		ret = IMP_AI_EnableHpf(&pdev->audioIAttr);
		if(ret != 0){
			IMP_LOG_ERR(TAG,"%s: enable hpf is fail \n", __func__);
		}
	}else{
		IMP_LOG_ERR(TAG,"%s: HP_AI is disbale\n", __func__);
	}
	return 0;
}
int IMP_AI_DisableAlgo(int audioDevId, int aiChn)
{
	int ret = -1;
	AudioInDev *pdev = NULL;
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}

	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(pch->ai_ns_enable_suppression == 1)
	{
		IMP_AI_DisableNs();
	}
	if(pch->ai_agc_enable_suppression == 1)
	{
		IMP_AI_DisableAgc();
	}
	if(pch->ai_hpf_enable_suppression == 1)
	{
		IMP_AI_DisableHpf();
	}
	return 0;
}
int IMP_AI_SetVolMute(int audioDevId, int aiChn, int mute)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (mute == 1) {
		pch->vol.vol = 0.0;
		pch->vol.mute = mute;
	} else if (mute == 0) {
		pch->vol.vol = pch->ai_vol_coef;
		pch->vol.mute = mute;
	} else {
		IMP_LOG_ERR(TAG, "Invalid AI Mute Value.\n");
		return -1;
	}

	return 0;
}

int IMP_AI_SetVol(int audioDevId, int aiChn, int aiVol)
{
	IMPAudioVol vol;
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI Set Vol: %d\n", aiVol);
	vol.minVol = -30;
	vol.maxVol = 120;
	vol.mute   = 0;
	if (aiVol < vol.minVol) aiVol = -30;
	if (aiVol > vol.maxVol) aiVol = 120;
	if (aiVol == -30) {
		vol.vol = 0.0;
	} else {
		/*dB = 20log(Vx / Vo), step is 0.5dB*/
		vol.vol = pow(10, ((((double)aiVol) * 0.5) - 30.0) / 20.0);
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	pch->vol.vol = vol.vol;
	pch->vol.minVol = vol.minVol;
	pch->vol.maxVol = vol.maxVol;
	pch->vol.mute = vol.mute;
	pch->ai_vol_coef = vol.vol;
	pch->ai_vol = aiVol;
	return 0;
}

int IMP_AI_GetVol(int audioDevId, int aiChn, int *vol)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if (vol == NULL) {
		IMP_LOG_ERR(TAG, "vol is NULL.\n");
		return -1;
	}
	*vol = pch->ai_vol;
	IMP_LOG_INFO(TAG, "AI Get Vol: %d\n", *vol);
	return 0;
}

int IMP_AI_SetGain(int audioDevId, int aiChn, int aigain)
{
	int ret = -1;
	AudioInChn *pch = NULL;
	AudioInDev *pdev = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI Set Gain: %d\n", aigain);
	/**********************************************\
	| vol	  0	   1	 2	  ..	30  31	       |
	| again: -18  -16.5  -15  ..	27  28.5 (+DB) |
	\**********************************************/
	/**********************************************\
	| vol :   0	   1	 2	  ..	30    31	   |
	| dgain: -97  -96.5  -96  ..	29.5  30 (+DB) |
	\**********************************************/
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	//analog gain
	if (aigain < 0)
		aigain = 0;
	if (aigain > 31)
		aigain = 31;
	if ((ret = __ai_dev_set_gain(pdev, aigain)) != 0) {
		IMP_LOG_ERR(TAG, "err: __ai_dev_set_gain\n");
	}
	pch->ai_ch_gain = aigain;
	return 0;
}

int IMP_AI_GetGain(int audioDevId, int aiChn,int *aigain)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	*aigain = pch->ai_ch_gain;
	IMP_LOG_INFO(TAG, "AI Get Gain: %d\n", *aigain);
	return 0;
}

int IMP_AI_SetDigitalGain(int audioDevId, int aiChn, int dgain)
{
	int ret = -1;
	AudioInChn *pch = NULL;
	AudioInDev *pdev = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI Set Digital Gain: %d\n", dgain);
	/**********************************************\
	| vol :   0	   1	 2	  ..	254   255	   |
	| dgain: -97  -96.5  -96  ..	29.5  30 (+DB) |
	\**********************************************/
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	//analog gain
	if (dgain < 0)
		dgain = 0;
	if (dgain > 255)
		dgain = 255;
	if ((ret = __ai_dev_set_dgain(pdev, dgain)) != 0) {
		IMP_LOG_ERR(TAG, "err: __ai_dev_set_dgain\n");
	}
	pch->ai_ch_dgain = dgain;
	return 0;
}

int IMP_AI_GetDigitalGain(int audioDevId, int aiChn,int *dgain)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	*dgain = pch->ai_ch_dgain;
	IMP_LOG_INFO(TAG, "AI Get Gain: %d\n", *dgain);
	return 0;
}
#if 0
int IMP_AI_EnableAlc(int audioDevId, int aiChn)
{
	AudioInChn *pch = NULL;
	AudioInDev *pdev = NULL;
	int ret = -1;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(pch->ai_ch_alc_en){
		IMP_LOG_ERR(TAG, "AI codec Alc is already enabled.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI codec Alc enable.\n");
	if ((ret = ioctl(pdev->aifd, AMIC_AI_ENABLE_ALC_L, 1)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: disable alc left track %s\n", strerror(errno));
		return -1;
	}
	if ((ret = ioctl(pdev->aifd, AMIC_AI_ENABLE_ALC_R, 1)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: disable alc left track %s\n", strerror(errno));
		return -1;
	}
	pch->ai_ch_alc_en = 1;
	return ret;
}

int IMP_AI_DisableAlc(int audioDevId, int aiChn)
{
	AudioInChn *pch = NULL;
	AudioInDev *pdev = NULL;
	int ret = -1;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!pch->ai_ch_alc_en){
		IMP_LOG_ERR(TAG, "AI codec Alc is not enabled.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI codec Alc disable.\n");
	if ((ret = ioctl(pdev->aifd, AMIC_AI_ENABLE_ALC_L, 0)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: disable alc left track %s\n", strerror(errno));
		return -1;
	}
	if ((ret = ioctl(pdev->aifd, AMIC_AI_ENABLE_ALC_R, 0)) != 0) {
		IMP_LOG_ERR(TAG, "ai err: disable alc left track %s\n", strerror(errno));
		return -1;
	}
	pch->ai_ch_alc_en = 0;
	return ret;
}

int IMP_AI_SetAlcGain(int audioDevId, int aiChn, int maxgain, int mingain)
{
	int ret = -1;
	AudioInChn *pch = NULL;
	AudioInDev *pdev = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	/*****************************************************\
	| maxvol -13.5  7.5  1.5  4.5  10.5  16.5  22.5  28.5 |
	|  gain: 0      1    2    3    4     5     6     7    |
	\*****************************************************/
	/******************************************\
	| maxvol -18  -12  6   0   6   12   18  24 |
	|  gain: 0    1    2   3   4   5    6   7  |
	\******************************************/

	pdev = &aiDev[audioDevId];
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!pch->ai_ch_alc_en){
		IMP_LOG_ERR(TAG, "Enable Alc function first.\n");
		return -1;
	}
	if (maxgain < 0)
		maxgain = 0;
	if (maxgain > 7)
		maxgain = 7;
	if (mingain < 0)
		mingain = 0;
	if (mingain > 7)
		mingain = 7;
	IMP_LOG_INFO(TAG, "AI set alc maxGain: %d minGain: %d\n", maxgain,mingain);
	if ((ret = __ai_dev_set_alcgain(pdev, maxgain, mingain)) != 0) {
		IMP_LOG_ERR(TAG, "err: __ai_dev_set_pga_gain\n");
	}

	return 0;
}

int IMP_AI_GetAlcGain(int audioDevId, int aiChn, int *maxgain, int *mingain)
{
	AudioInChn *pch = NULL;
	if (audioDevId > MAX_AI_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ai device ID.\n");
		return -1;
	}
	if (aiChn > MAX_AI_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ai channel.\n");
		return -1;
	}
	pch = &aiDev[audioDevId].audioInChn[aiChn];
	if(!pch->ai_ch_alc_en){
		IMP_LOG_ERR(TAG, "Enable Alc function first.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AI set alc maxGain: %d minGain: %d\n", maxgain,mingain);
	*maxgain = pch->ai_ch_alc_maxgain;
	*mingain = pch->ai_ch_alc_mingain;
	return 0;
}
#endif


